本帖最后由 slytherinsun 于 2025-8-29 23:55 编辑
1.简介
本次测评在前两篇的FreeRTOS的基础上,通过开发板的Key1按键触发外部中断,在中断中给Led_Thread任务发送事件。Led_Thread任务等待事件,并在事件发生后改变LED2的状态,同时通过调整定时器4的比较值来改变PWM的占空比。定时器4的PWM的输出连接到LED3,根据正弦序列从0 - 100%生成了11个占空比,通过按键触发不同的占空比来实现调整亮度的目的,同时可用于PWM风扇的调速控制。
2.定时器4通道2输出PWM
通过查阅原理图可知APM32F402R开发板的LED3连接的是PB7引脚,在数据手册中此引脚的其中一个默认复用功能是TMR4_CH2,
pwm01
pwm02
通用定时器4支持PWM输出,由此可以实现基于PWM占空比的亮度或速度调节。其周期由自动重装载寄存器(TMR4_AUTORLD)的值决定,
信号脉宽由比较寄存器(TMR4_CC2)的值决定。其向上、向下计数和中央对齐模式及PWM模式1/2对亮度调整影响不大,本次测试使用PWM模式1向上计数。
根据数据手册的时钟树及频率配置可知,TMR4时钟为APB1 x 2 = 120MHz。PWM的配置基于官方例程PWM输出代码进行修改。
pwm03
pwm04
当前配置PWM为10KHz,按照正弦序列将亮度从0 - 100%分了10份共11个比较寄存器值。具体代码如下:
- void TMR_Config(void)
- {
- TMR_BaseConfig_T tmrBaseConfig;
- TMR_OCConfig_T tmrOCConfig;
- RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR4);
- /* TMR4 clock source frequency = 120MHz */
- tmrBaseConfig.countMode = TMR_COUNTER_MODE_UP;
- tmrBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
- tmrBaseConfig.period = 999; //tmr update rate = 10M / (999 + 1) = 10K
- tmrBaseConfig.division = 19; //tmr4 clk 10M
- tmrBaseConfig.repetitionCounter = 0;
- TMR_ConfigTimeBase(TMR4, &tmrBaseConfig);
- /* 50% PWM */
- tmrOCConfig.mode = TMR_OC_MODE_PWM1;
- tmrOCConfig.outputState = TMR_OC_STATE_ENABLE;
- tmrOCConfig.outputNState = TMR_OC_NSTATE_DISABLE;
- tmrOCConfig.polarity = TMR_OC_POLARITY_HIGH;
- tmrOCConfig.nPolarity = TMR_OC_NPOLARITY_HIGH;
- tmrOCConfig.idleState = TMR_OC_IDLE_STATE_RESET;
- tmrOCConfig.nIdleState = TMR_OC_NIDLE_STATE_RESET;
- tmrOCConfig.pulse = PWM_Arry[0];
- TMR_ConfigOC2(TMR4, &tmrOCConfig);
- TMR_EnablePWMOutputs(TMR4);
- TMR_Enable(TMR4);
- }
3.按键触发中断
通过查阅原理图可知用户按键连接到了PA1上,按下时为低电平。在用户手册中查到PA1内部连接到了EINT1,可以在程序中配置PA1为上拉输入,开放EINT_LINE_1的中断请求,并设置为下降沿触发,然后设置中断优先级,开启中断。最后在中断处理函数中清除中断标志位,并给Led_Thread任务发送事件。
pwm05
pwm06
按键中断的配置使用例程中的void BOARD_BUTTON_Config(BOARD_BUTTON_T button, BOARD_BUTTON_MODE_T mode)函数,为适应FreeRTOS环境,需要把配置NVIC的代码修改为支持RTOS的接口:
- void BOARD_BUTTON_Config(BOARD_BUTTON_T button, BOARD_BUTTON_MODE_T mode)
- {
- GPIO_Config_T GPIO_ConfigStruct = {0U};
- EINT_Config_T EINT_ConfigStruct = {0U};
-
- /* Enable the BUTTON Clock */
- RCM_EnableAPB2PeriphClock(BUTTON_CLK[button] | RCM_APB2_PERIPH_AFIO);
-
- /* Configure Button pin as input floating */
- GPIO_ConfigStruct.mode = GPIO_MODE_IN_PU;
- GPIO_ConfigStruct.pin = BUTTON_PIN[button];
- GPIO_Config(BUTTON_PORT[button], &GPIO_ConfigStruct);
-
- if (mode == BUTTON_MODE_EINT)
- {
- /* Connect Button EINT Line to Button GPIO Pin */
- GPIO_ConfigEINTLine(BUTTON_PORT_SOURCE[button], BUTTON_PIN_SOURCE[button]);
- /* Configure Button EINT line */
- EINT_ConfigStruct.line = BUTTON_EINT_LINE[button];
- EINT_ConfigStruct.mode = EINT_MODE_INTERRUPT;
- EINT_ConfigStruct.trigger = EINT_TRIGGER_FALLING;
- EINT_ConfigStruct.lineCmd = ENABLE;
- EINT_Config(&EINT_ConfigStruct);
- /* Enable and set Button EINT Interrupt to the lowest priority */
- // NVIC_EnableIRQRequest(BUTTON_IRQn[button], 0x0F, 0x0F);
- NVIC_SetPriority (BUTTON_IRQn[button], 1U);
- NVIC_EnableIRQ(BUTTON_IRQn[button]);
- }
- }
并在apm32f402_403_int.c中编写中断服务函数:
- void EINT1_IRQHandler(void)
- {
- if (EINT_ReadIntFlag(EINT_LINE_1))
- {
- EINT_ClearIntFlag(EINT_LINE_1);
- osEventFlagsSet(testEveGroupID, 0x1);
- }
- }
为使中断服务函数能够调用RTOS接口,需要包含cmsis_os2.h头文件,并把调用到的任务和事件相关变量声明为外部变量。
4.PWM调光任务
PWM调光任务修改自FreeRTOS例程中的Led_Thread,任务中等待按键中断的事件,收到事件后切换LED2的状态,并打印按键消息,之后根据接收到的事件次数依次调用PWM_Arry数组中预定义的值切换PWM占空比来实现亮度调节的目的。具体代码如下:
- uint16_t PWM_Arry[11] = {999, 712, 590, 493, 409, 333, 262, 194, 128, 64, 0};
- void Led_Thread(void *argument)
- {
- UNUSED(argument);
- uint32_t cnt = 0;
- while (1)
- {
- /* Toggle LED2 */
- osEventFlagsWait(testEveGroupID, 0x1, osFlagsWaitAll, osWaitForever);
- BOARD_LED_Toggle(LED2);
- // osDelay(600);
- printf("get key push\r\n");
- cnt++;
- if(cnt > 11) {
- cnt = 0;
- }
- TMR_ConfigCompare2(TMR4, PWM_Arry[cnt]);
- }
- }
5.演示
代码编译完成烧录后,可以在日志中看到环境信息采集任务定时打印的气压、温湿度数据,按下Key1后可以看到获取按键消息的打印,开发板上的LED3随着按下次数循环改变亮度。
pwm07
|