stm32F103上基于FreeRTOS系统的亮度可调小台灯

tech2026-03-09  4

目录

在FreeRTOS系统上做一个亮度可调的小台灯项目要求项目拆解结果展示使用工具参考历程主要代码

在FreeRTOS系统上做一个亮度可调的小台灯

项目要求

1)小台灯可以手动进行点亮和熄灭; 2)小台灯具有手动切换模式功能,模式一“强光”,此时两个LED 灯发光;模式二“弱光”,此时一个LED灯发光; 3)小台灯亮度可调,在模式一时,两个LED灯同时可调,在模式二时,仅有一个LED灯可调; 4)小台灯功能按键被按下时,蜂鸣器“滴答”一声; 5)定时器记录小台灯工作时间,并发送至串口;

项目拆解

对于板子上面的三个按键,进行功能划分:

按键KEY_0:控制LED0的工作。包括四个挡位,暗、中、亮以及灭,每按一下会按照顺序对应切换到下一个挡位。对应蜂鸣器“哔——”一声。按键KEY_1: 控制LED1和LED0共同工作。分为同时亮和同时灭两个挡位,随按键按下进行切换。对应蜂鸣器“哔——两声”。按键KEY_UP: 当小灯工作时,记录该模式工作的时间,当这个按键按下时,向串口发送当前工作模式的工作时间。当工作模式切换时,将从零计数。

结果展示

使用工具

keil5c语言正点原子STM32F103开发板FreeRTOS

参考历程

蜂鸣器按键输入定时器中断pwm输入实验FreeRTOS任务运行时间统计实验

主要代码

//<main.c> #include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "timer.h" #include "lcd.h" #include "beep.h" #include "key.h" #include "string.h" #include "FreeRTOS.h" #include "task.h" #define FreeRTOSRunTimeTicks 1 //任务优先级 #define START_TASK_PRIO 1 //任务堆栈大小 #define START_STK_SIZE 128 //任务句柄 TaskHandle_t StartTask_Handler; //任务函数 void start_task(void *pvParameters); //任务优先级 #define TASK1_TASK_PRIO 2 //任务堆栈大小 #define TASK1_STK_SIZE 128 //任务句柄 TaskHandle_t Task1Task_Handler; //任务函数 void task1_task(void *pvParameters); //任务优先级 #define TASK2_TASK_PRIO 2 //任务堆栈大小 #define TASK2_STK_SIZE 128 //任务句柄 TaskHandle_t Task2Task_Handler; //任务函数 void task2_task(void *pvParameters); //任务优先级 #define RUNTIMESTATS_TASK_PRIO 3 //任务堆栈大小 #define RUNTIMESTATS_STK_SIZE 128 //任务句柄 TaskHandle_t RunTimeStats_Handler; //任务函数 void RunTimeStats_task(void *pvParameters); char RunTimeInfo[1000]; //保存任务运行时间信息 int rtime=0; int main(void) { delay_init(); //延时函数初始化 uart_init(115200); //初始化串口 LED_Init(); //初始化LED KEY_Init(); //初始化按键 BEEP_Init(); //初始化蜂鸣器端口 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 TIM3_PWM_Init(399,719); //time=4ms ARR=399 PSC=719 time=(arr+1)(psc+1)/Tclk //创建开始任务 xTaskCreate((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint16_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t* )&StartTask_Handler); //任务句柄 vTaskStartScheduler(); //开启任务调度 } //开始任务任务函数 void start_task(void *pvParameters) { taskENTER_CRITICAL(); //进入临界区 //创建TASK1任务 xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1_task", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); //创建TASK2任务 xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2_task", (uint16_t )TASK2_STK_SIZE, (void* )NULL, (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t* )&Task2Task_Handler); //创建RunTimeStats任务 xTaskCreate((TaskFunction_t )RunTimeStats_task, (const char* )"RunTimeStats_task", (uint16_t )RUNTIMESTATS_STK_SIZE, (void* )NULL, (UBaseType_t )RUNTIMESTATS_TASK_PRIO, (TaskHandle_t* )&RunTimeStats_Handler); vTaskDelete(StartTask_Handler); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区 } //task1任务函数 void task1_task(void *pvParameters) { vu8 key=0; u8 led0pwmval=0; vu8 key_forward=0; //LED0=1; while(1) { key=KEY_Scan(0); //得到键值 if(key) { switch(key) { case KEY0_PRES: //控制LED0 TIM2_Int_Init(50-1,72-1); //初始化TIM3 BEEP=!BEEP; delay_ms(200); BEEP=!BEEP; LED1=1; if(key_forward==KEY1_PRES) { led0pwmval=0; } if(led0pwmval>3) led0pwmval=0; led0pwmval++; if(led0pwmval==1) { TIM_SetCompare2(TIM3,10); TIM2_Int_Init(50-1,72-1); rtime=0; TIM_Cmd(TIM2, ENABLE); }//使能TIMx else if(led0pwmval==2) { TIM_SetCompare2(TIM3,100); TIM2_Int_Init(50-1,72-1); rtime=0;TIM_Cmd(TIM2, ENABLE); } //使能TIMx else if(led0pwmval==3) { TIM_SetCompare2(TIM3,399); TIM2_Int_Init(50-1,72-1); rtime=0; TIM_Cmd(TIM2, ENABLE); } //使能TIMx else { TIM_SetCompare2(TIM3,0); rtime=0; TIM_Cmd(TIM2, DISABLE); } break; } key_forward=key; } } } //task2任务函数 void task2_task(void *pvParameters) { vu8 key=0; u8 led1pwmval=0; vu8 key_forward=0; // LED0=1; // LED1=1; while(1) { key=KEY_Scan(0); //得到键值 if(key) { switch(key) { case KEY1_PRES: //同时控制LED0,LED1 TIM2_Int_Init(50-1,72-1); //初始化TIM3 TIM_Cmd(TIM2, ENABLE); //使能TIMx BEEP=!BEEP; delay_ms(150); BEEP=!BEEP; delay_ms(100); BEEP=!BEEP; delay_ms(150); BEEP=!BEEP; if(key_forward==KEY0_PRES) { led1pwmval=0; } led1pwmval++; if(led1pwmval%2) { LED1=0; TIM_SetCompare2(TIM3,399); TIM2_Int_Init(50-1,72-1);rtime=0; TIM_Cmd(TIM2, ENABLE); } else { LED1=1; TIM_SetCompare2(TIM3,0);rtime=0; TIM_Cmd(TIM2, DISABLE); } break; } key_forward=key; } } } //RunTimeStats任务 void RunTimeStats_task(void *pvParameters) { u8 key=0; float i=0; while(1) { key=KEY_Scan(0); if(key==WKUP_PRES) { BEEP=!BEEP; delay_ms(200); BEEP=!BEEP; i=(float)rtime*0.00005; printf("The light runtime is %fs\r\n",i); } vTaskDelay(10); //延时10ms,也就是1000个时钟节拍 } } ```javascript // <timer.c> #include "timer.h" #include "led.h" #include "usart.h" // //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK战舰STM32开发板 //定时器 驱动代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //修改日期:2012/9/4 //版本:V1.1 //版权所有,盗版必究。 //Copyright(C) 广州市星翼电子科技有限公司 2009-2019 //All rights reserved //******************************************************************************** //V1.1 20120904 //1,增加TIM3_PWM_Init函数。 //2,增加LED0_PWM_VAL宏定义,控制TIM3_CH2脉宽 // extern rtime; //FreeRTOS时间统计所用的节拍计数器 volatile unsigned long long FreeRTOSRunTimeTicks; //TIM3 PWM部分初始化 //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟 GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO //初始化TIM3 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 //初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2 TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIM3 } //初始化TIM3使其为FreeRTOS的时间统计提供时基 void ConfigureTimeForRunTimeStats(void) { //定时器3初始化,定时器时钟为72M,分频系数为72-1,所以定时器3的频率 //为72M/72=1M,自动重装载为50-1,那么定时器周期就是50us // FreeRTOSRunTimeTicks=0; // TIM2_Int_Init(50-1,72-1); //初始化TIM3 } //通用定时器3中断初始化 //这里时钟选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 //这里使用的是定时器2! void TIM2_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能 //定时器TIM2初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级4级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 TIM_Cmd(TIM2, DISABLE); //使能TIMx } //定时器2中断服务函数 void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中断 { rtime=rtime+1; } TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位 }

最后感谢和我一起做这个项目的朋友,也感谢你们愿意让我分享我们的成果。

最新回复(0)