更改

跳转至: 导航搜索

NBDK-L4:基础实验教程

添加12,277字节2019年1月23日 (三) 17:20
STM32L476 计时器捕获简介
红外接收实验,是利用开发板上的红外接收传感器,去获取遥控器按下的信号,红外传感器获取到这个信号后,会转成一段PWM波形从它的DATA引脚输出。此时我们利用STM32的定时器捕获功能,就可以获取到这个PWM波形所携带的信息,以此判断遥控器按下的是哪个按键。
=== STM32L476 计时器捕获简介 定时器捕获简介 ===以下部分为TIM2 / TIM3 / TIM4 / TIM5这四个定时器的介绍。 1.通用定时器简介: 通用定时器由一个由可编程预分频器驱动的16位或32位自动重载计数器组成。它们可用于各种目的,包括测量输入信号的脉冲长度(输入捕获)或生成输出波形(输出比较和PWM)。 使用定时器预分频器和RCC时钟控制器预分频器,可以将脉冲长度和波形周期从几微秒调制到几毫秒。定时器完全独立,不共享任何资源。  2.通用定时器功能 •16位(TIM3,TIM4)或32位(TIM2和TIM5)上,下,上/下自动重载计数器。 •16位可编程预分频器,用于分频(也“在运行中”)计数器时钟 频率由1到65535之间的任何因子组成。 •最多4个独立频道: - 输入捕获 - 输出比较 - PWM生成(边缘和中心对齐模式) - 单脉冲模式输出 •同步电路,用外部信号控制定时器并互连 几个计时器。 •以下事件的中断/ DMA生成: - 更新:计数器溢出/下溢,计数器初始化(通过软件或 内部/外部触发器) - 触发事件(计数器启动,停止,初始化或通过内部/外部触发计数) - 输入捕获 - 输出比较 •支持增量(正交)编码器和霍尔传感器电路进行定位 目的 •外部时钟或逐周期电流管理的触发输入 3.通用定时器捕获模式 在输入捕捉模式下,捕捉/比较寄存器(TIMx_CCRx)用于在相应''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''ICx信号检测到转换后锁存计数器的值。 发生捕获时,会设置相应的CCXIF标志(TIMx_SR''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''寄存器),如果使能了中断或DMA请求,则可以发送它们。 如果在C''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''CxIF标志已经为高电平时发生捕获,则设置过''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''捕获标志CCxOF(TI''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''Mx_SR寄存器)''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''。 CCxIF可以''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''通过软件将其写入0或读取存储在TIMx_CC''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''Rx寄存器中的捕获数据来''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''清除。 将其写入0时,CCxOF将被清除。''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''
=== 硬件设计 ===
选择STM32L4引脚PC6用来捕获红外传感器HS0038的DATA引脚输出的PWM波。''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''选择STM32L4引脚PC6用来捕获''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''红外传感器HS0038的DATA引脚输出的PWM波。[[文件:NBDK-SCH-IR.png|边框|居中|无框|393x393像素|''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'']]
=== 实验准备 ===
# 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''开发板。# 使用miniUSB线,连接PC与开发板USB接口。使''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''用miniUSB线''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'',连接PC与开发板USB接口。# 将SW1拨到DBG端,SW2拨到MCU。将SW1拨到D''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''BG端,''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''SW2拨到MCU。# 使用Keil打开基础实验 使用Keil打''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''开基础实验 09-红外接收实验工程。红外接收''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''实验工程。
# 下载程序,并完成功能测试。
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了串口部分,目的是打印采集到的温湿度数据。接下来我们初始化LCD的SPI控制引脚,LCD背光引脚,并且初始化LCD的图形控制界面。 然后我们在LCD上打印固定的遥控器按键显示格式,也就是"irBtnVal"、"irBtnCnt"、"irBtnInfo"这几个字符串。
接下来初始化I2C引脚。接下来我们初始化我们此实验的重点功能,也就是TIM3定时器。
在while()循环中,我们每隔500ms采集一次温湿度的值,并且将采集的温湿度值转化成真实值,格式化打印到串口显示。循环中,我们轮询遥控器的按键信息,一旦有遥控器按下,则在LCD的对应位置,打印按键的信息。
<syntaxhighlight lang="c++" line="1" start="3334">
int main(void)
{
irInfo_t irkey = {0,0};
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
// 重置所有外设、flash界面以及系统时钟
// 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
SystemClock_Config();
// 初始化串口USART1
MX_USART1_UART_Init();
// 初始化I2C2LCD SPI初始化 MX_I2C2_InitLCD_GPIO_Init(); // LCD IO控制引脚(例如背光) MX_SPI1_Init(); // LCD SPI控制引脚 // 图形界面初始化 GUI_Init(); // GUI界面初始化 GUI_Clear(); // 清屏 GUI_SetColor(GUI_Crimson); // 红色字体 GUI_DispStringAt("irBtnVal:",24,24); // 打印字符串"irBtnVal:"到位置X->24,Y->24 GUI_DispStringAt("irBtnCnt:",24,72); // 打印字符串"irBtnCnt:"到位置X->24,Y->96 GUI_DispStringAt("irBtnInfo:",24,120); // 打印字符串"irBtnCnt:"到位置X->24,Y->120  // 初始化TIM3 MX_TIM3_Init();
//
while (1)
{
HAL_Delayirkey = IRBNT_POLL(500); // 轮训获取IR按键信息 if(irkey.irBtnVal) // 如果按键信息存在 { GUI_DispHexAt(irkey.irBtnVal,144,24,2); printf// 打印按键值 GUI_DispHexAt(irkey.irBtnCnt,144,72,2); // 打印按键计数 // 打印按键图标或名称 GUI_GotoXY(144,120); // 指的光标位置 GUI_ClearArea(); // 清除指定位置数据 switch(irkey.irBtnVal) // 判断按键值,打印相应图标或名称 { case REMOTE_BTN_SWITCH: GUI_DispString("'switch'"); break; case REMOTE_BTN_MENU: GUI_DispString("'menu'"); break; case REMOTE_BTN_MUTE: GUI_DispString("'mute'"); break; case REMOTE_BTN_MODE: GUI_DispString("Temp = %.1f\r\n'mode'"); break; case REMOTE_BTN_PLUS: GUI_DispString("'+'"); break; case REMOTE_BTN_RETURN: GUI_DispString("'return'"); break; case REMOTE_BTN_REWIND: GUI_DispString("'|<<'"); break; case REMOTE_BTN_PAUSE: GUI_DispString("'>||'"); break; case REMOTE_BTN_FASTFORWARD: GUI_DispString("'>>|'",SHT20_Convert); break; case REMOTE_BTN_0: GUI_DispString(SHT20_ReadTemp"'0'"); break; case REMOTE_BTN_LESS: GUI_DispString("'-'"),; break; case REMOTE_BTN_OK: GUI_DispString("'OK'"); break; case REMOTE_BTN_1: GUI_DispString("'1'"); break; case REMOTE_BTN_2: GUI_DispString("'2'"); break; printf case REMOTE_BTN_3: GUI_DispString("'3'"); break; case REMOTE_BTN_4: GUI_DispString("'4'"); break; case REMOTE_BTN_5: GUI_DispString("RH = %.1f%%\r\n'5'",SHT20_Convert); break; case REMOTE_BTN_6: GUI_DispString(SHT20_ReadRH"'6'"); break; case REMOTE_BTN_7: GUI_DispString("'7'"),0; break; case REMOTE_BTN_8: GUI_DispString("'8'"); break; case REMOTE_BTN_9: GUI_DispString("'9'"); break; } }
}
}
</syntaxhighlight>
==== gyu_i2cgyu_irc ====<syntaxhighlight lang="c++" line="1" start="59">void MX_TIM3_Init(void){ htim3.Instance = TIM3; // 通用定时器3 htim3.Init.Prescaler = 80-1; // TIM3 80预分频器(APB2总线),80MHz / 80 = 1MHz(1us) htim3.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数器 htim3.Init.Period = 10000; // 自动装载值设为10000,装满一次 10000 * 1us = 10ms htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;// 不分频 htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 自动加载使能 if (HAL_TIM_IC_Init(&htim3) != HAL_OK) // 初始化TIM3,出错则进入错误处理函数 { _Error_Handler(__FILE__, __LINE__); } // 初始化TIM3输入捕获参数 TIM_IC_InitTypeDef sConfigIC; // 定义输入捕获结构体(IC:Input capture) sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE; // 上升沿下降沿都捕获 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 配置为TI1 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不分频 sConfigIC.ICFilter = 0x03; // IC1F=0011 8个定时器时钟周期滤波 // 初始化TIM3 CH1通道,出错则进入错误处理函数 if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } HAL_TIM_Base_Start_IT(&htim3); // 使能更新中断(也就是TIM_IT_UPDATE) HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开始捕获TIM3 CH1}</syntaxhighlight><syntaxhighlight lang="c ++" line="1" start="96">void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim){ GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_TIM3_CLK_ENABLE(); // 使能TIM3时钟 __HAL_RCC_GPIOC_CLK_ENABLE(); // 开启GPIOC时钟 GPIO_Initure.Pin = GPIO_PIN_6; // PC6 GPIO_Initure.Mode = GPIO_MODE_AF_PP; // 推挽输出 GPIO_Initure.Pull = GPIO_PULLUP; // 上拉 GPIO_Initure.Speed =GPIO_SPEED_HIGH; // 高速模式 GPIO_Initure.Alternate =GPIO_AF2_TIM3; // PC6配置为TIM3通道1 HAL_GPIO_Init(GPIOC, &GPIO_Initure); HAL_NVIC_SetPriority(TIM3_IRQn, 10, 0); // 设置TIM3中断优先级 HAL_NVIC_EnableIRQ(TIM3_IRQn); // 使能TIM3中断}</syntaxhighlight><syntaxhighlight lang="c++" line="1" start="122">void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim->Instance == TIM3) { if(irStatus & IR_STATUS_BootCode) // 如果接收到引导码 { irStatus &= ~IR_STATUS_Rising; // 删除上升沿标记(以防止本次出错,确保下次采集流程正确) if(tim3Cnt == 0) // 如果是第一次进入(计数为0) { irStatus |= IR_STATUS_BtnInfo; // 记录已经获取到IR按键信号(遥控器按键值) } if((tim3Cnt & 0X0F) < 15) // 进入回调少于15次 { tim3Cnt++; // 计数值自加 } else // 超过15次,代表依次采集超时(不论是否成功) { irStatus &= ~IR_STATUS_BootCode;// 删除引导码标记 tim3Cnt = 0; // 清除计数值 } } }}</syntaxhighlight><syntaxhighlight lang="c++" line="1" start="155">void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ // 判断是否为TIM3 CH1捕获产生的回调 if((htim->Instance == TIM3) && (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)) { if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_6)) // 获取PC6引脚电平,如果是高电平,则代表是上升沿捕获 { __HAL_TIM_SET_COUNTER(&htim3, 0); // 清除TIM3定时器计数值 irStatus |= IR_STATUS_Rising; // 标记上升沿捕获 } else //如果是低电平,则代表是下降沿触发 { tim3Val = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1); // 读取TIM3 CH1定时器计数值 if(irStatus & IR_STATUS_Rising) // 存在上升沿标记,我们比较定时器计数值 { if(tim3Val > 260 && tim3Val < 860) // 560us代表低电平,范围560us±300us { irRecData <<= 1; // 左移一位 irRecData |= 0; // bit位赋值0 } else if(tim3Val > 1380 && tim3Val < 1980) // 1680us代表高电平,范围1680us±300us { irRecData <<= 1; // 左移一位 irRecData |= 1; // bit位赋值1 } else if(tim3Val > 2200 && tim3Val < 2800) // 2500us代表本次按键结束,范围2500us±300us { irCnt++; // 按键次数新增1 tim3Cnt = 0; // 清除计数值 } else if(tim3Val > 4200 && tim3Val < 4800) // 4500us代表新的按键,范围4500us±300us { irStatus |= IR_STATUS_BootCode; // 标记引导码 irCnt = 0; // 有新的按键到来,清除按键计数 } } irStatus &= ~IR_STATUS_Rising; // 清除上升沿标记 } }}</syntaxhighlight><syntaxhighlight lang="c++" line="1" start="206">irInfo_t IRBNT_POLL(void){ irInfo_t irinfo = {0,0}; // 定义按键信息结构体 uint8_t bcode, dcode; // 定义引导码正反编码 uint8_t bvalue, dvalue; // 定义按键值正反编码 if(irStatus & IR_STATUS_BtnInfo) // 如果获取到按键值 { bcode = irRecData >> 24; // 引导码 dcode = (irRecData >> 16) & 0xff; // 引导码反编码  if((bcode == (uint8_t)~dcode) && bcode == REMOTE_DEVICE_ID) // 判断引导码是否正确 { bvalue = irRecData >> 8; // 按键值 dvalue = irRecData; // 按键值反编码 if(bvalue == (uint8_t)~dvalue) // 判断按键值是否正确 { irinfo.irBtnVal = bvalue; // 将按键值赋给irinfo.irBtnVal } } irinfo.irBtnCnt = irCnt; // 将按键此处赋给irinfo.irBtnCnt }  return irinfo; // 返回按键信息}</syntaxhighlight>
[[分类:NB-IOT]]
[[分类:NBDK-L4]]
[[分类:教程]]
__强显目录__
510
个编辑

本PDF由谷雨文档中心自动生成,点击下方链接阅读最新内容。

取自“http://doc.iotxx.com/特殊:移动版差异/1372

导航菜单