前言:
? ? ? ? 信号指示灯,最好不要用单个的常量或常灭的形式指示,因为有可能是程序BUG造成程序宕机。
? ? ? ? 本篇用四个LED指示灯,LED1、LED2、LED3、LED4,如图
? ? ? ? 其中,LED1的作用是检测系统是否工作,以1s为周期的呼吸灯,当不闪烁的时候,说明系统故障。
? ? ? ? 其余三个指示灯,用于反应系统的事件。每个LED灯能反映5个事件,事件1:常亮,事件2:以200ms的周期闪烁,事件3:以500ms周期闪烁,事件4:以1s的周期闪烁,事件5:常灭。
? ? ? ? 用结构体命名,为sigLEDxx.light、sigLEDxx.l200ms、sigLEDxx.l500ms、sigLEDxx.l1s、sigLEDxx.ns? ?。其中xx是第几个灯的意思,比如第二个? sigLED02? ?。 后面的是周期时间。
正文:
? ? ? ? 打开上次建立的STM32CubeMX文件
1、配置4个LED灯的底层,看原理图
全是底边驱动,可以开漏输出,IO为低电平灯亮,反之灯灭。其中LED1--PB8、LED2--PB15、LED3--PA12、LED4--PB11。
1.2、配置STM32CubeMX,先单击第一处,再单击第二处
1.3、同样操作,PB15、PA12、PB11
?
1.3、把PB8、PB15、PA12、PB11配置成和下图相同,初始LED灯都是灭的
?1.4、一键跟新代码,并打开编译一下,无错误、无警告
?2、修改上次代码-由于上次代码 “? App_task();? ”放错位置,导致被系统删掉,重新添加
main.c文件的main()函数的死循环中,丢失后的代码,如下
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
/* USER CODE END 3 */
?修改补充,代码如下
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
App_task();
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
/* USER CODE END 3 */
3、在“APP”的目录下,新建一个文件夹“BSP”,在新建的文件夹里新建新的文件夹“Inc”和“Src”,把“BSP”添加到结构树中,把“BSP”下的“Inc”文件夹添加到“魔法棒” 头文件目录中,同“APP”操作
?
?4、建立“BspSigLED.c”文件并保存在“BSP”文件下的“Src”文件中,让后把文件“BspSigLED.c”添加在结构树的“BSP”目录下,建立“BspSigLED.h”文件并保存在“BSP”文件下的“Inc”文件中。同建立“AppTask”文件的操作
?5、“BspSigLED.h”的代码
#ifndef _BSPSIGLED_H_
#define _BSPSIGLED_H_
#include "AppTask.h"
typedef struct
{
unsigned char LED01; // 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
unsigned char LED02; // 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
unsigned char LED03; // 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
unsigned char LED04; // 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
}sigLEDk;
void SigLED(void);
#endif
?6、“BspSigLED.c”的代码,由于对时钟要求很低,所以用任务执行间隔事件充当时钟
#include "BspSigLED.h"
extern sigLEDk sigLED;
//灯光闪烁,指示系统事件运行,每隔50ms函数运行一次
void SigLED(void)
{
// 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭
static unsigned char time100ms,time250ms,time500ms;
time100ms++;
time250ms++;
time500ms++;
if(sigLED.LED01 == 1)//1
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);//IO输出低电平
}
else if(sigLED.LED01 == 0)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);//IO输出高电平
}
if(sigLED.LED02 == 1)//2
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
}
else if(sigLED.LED02 == 0)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
}
if(sigLED.LED03 == 1)//3
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
}
else if(sigLED.LED03 == 0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
}
if(sigLED.LED04 == 1)//4
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET);
}
else if(sigLED.LED04 == 0)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET);
}
if(time100ms>=2)//半个周期为100ms,T=200ms
{
time100ms=0;
if(sigLED.LED01 == 2)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//IO输出电平-高低电平反转
}
if(sigLED.LED02 == 2)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_15);
}
if(sigLED.LED03 == 2)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_12);
}
if(sigLED.LED04 == 2)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11);
}
}
if(time250ms>=5)//半个周期为250ms,T=500ms
{
time250ms=0;
if(sigLED.LED01 == 3)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
if(sigLED.LED02 == 3)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_15);
}
if(sigLED.LED03 == 3)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_12);
}
if(sigLED.LED04 == 3)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11);
}
}
if(time500ms>=10)//半个周期为500ms,T=1s
{
time500ms=0;
if(sigLED.LED01 == 4)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
if(sigLED.LED02 == 4)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_15);
}
if(sigLED.LED03 == 4)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_12);
}
if(sigLED.LED04 == 4)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11);
}
}
}
7、在“AppTask.h”的头文件中添加上? “? #include "BspSigLED.h"? ”头文件
#include "main.h"
#include "stdio.h"
#include "stdbool.h"
#include "adc.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "BspSigLED.h"
8、在“AppTask.c”的任务注册表中添加,该任务,非实时性任务,无需上电执行,每隔50ms执行一次
//任务注册表
static TaskCtrl_t TaskCtrl[] = //执行优先级程度 从上到下依次逐渐,第一个优先级最高,所有的任务函数都不能打断其它的任务函数
{
/* 【实时任务】 【上电立即运行】 【初始counter】 【任务周期ms】 【任务指针】*/ //时间调度最好是cycle_task的整数倍
{ 0, 0, 0/cycle_task, 500/cycle_task, taskTest},
{ 0, 0, 0/cycle_task, 50/cycle_task, SigLED},
};
9、整理一下,之前的代码
? ? ? ? 之前测试串口信息-接收到之后,再发出去。也放在时间片轮换调度系统中,实时性任务
原代码在main.c文件的main()函数的死循环中,如下,移植(原来位置不保留)到“AppTask.c”文件,新建一个测试函数
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
App_task();
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
/* USER CODE END 3 */
移植后
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
App_task();
}
/* USER CODE END 3 */
10、“AppTask.c”文件下添加代码,如下
//实时性任务
void taskTestTime(void)
{
unsigned char TxBuffTest[BUFFER_SIZE];
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
11、“AppTask.h”文件下给“? void taskTestTime(void);? ”函数声明,如下
void App_task(void);
void taskTest(void);
void taskTestTime(void);
12、建立初始化函数,此函数,仅上电的时候运行一次
12.1、“AppTask.c”文件下添加代码,如下
void AppInit(void)
{
}
12.2、“AppTask.h”文件下给“? void AppTask(void);? ”函数声明,如下
void App_task(void);
void taskTest(void);
void taskTestTime(void);
void AppInit(void);
12.3、在main.c文件下的main()函数中死循环上面的位置添加函数??AppInit();//用于初始化函数
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC,10);
AppInit();//用于初始化函数
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
13、在初始化的函数 “? AppInit();? ”里,输入指示灯的信号,检查指示灯的响应
sigLEDk sigLED;
void AppInit(void)
{
sigLED.LED01=1;
sigLED.LED02=2;
sigLED.LED03=3;
sigLED.LED04=4;
}
14、“AppTask.c”的代码为
#include "AppTask.h"
//任务注册表
static TaskCtrl_t TaskCtrl[] = //执行优先级程度 从上到下依次逐渐,第一个优先级最高,所有的任务函数都不能打断其它的任务函数
{
/* 【实时任务】 【上电立即运行】 【初始counter】 【任务周期ms】 【任务指针】*/ //时间调度最好是cycle_task的整数倍
{ 1, 0, 0, 100/cycle_task, taskTestTime},
{ 0, 0, 0/cycle_task, 500/cycle_task, taskTest},
{ 0, 0, 0/cycle_task, 50/cycle_task, SigLED},
};
#define TASK_MAX_NUM (sizeof(TaskCtrl)/sizeof(TaskCtrl[0])) //自动生成任务调度的数量给 TASK_MAX_NUM
void App_task(void)
{
volatile unsigned char i_task, j_task;
static unsigned int timen_task;
if(timen - timen_task >= cycle_task)//每间隔 cycle_task ms 执行一次
{
timen_task = timen;//和上面if括号的那句共同构成 每隔cycle_task个定时周期更新一次任务等待时长
for(i_task = 0; i_task < TASK_MAX_NUM; i_task++)//把所有的任务检测一边
{
if((TaskCtrl[i_task].Task_Counter > 0)&&(TaskCtrl[i_task].Task_RealTime == 0))//用于防止初始计数值为零,自减溢出
TaskCtrl[i_task].Task_Counter--;
if((TaskCtrl[i_task].Task_Counter == 0)&&(TaskCtrl[i_task].Task_RealTime == 0))//自减为零的时候,认为该任务可以执行
{
TaskCtrl[i_task].Task_Running = true;//认为为可执行任务
TaskCtrl[i_task].Task_Counter = TaskCtrl[i_task].Task_PeriodTime;//更新计时周期
}
}
}
for(j_task = 0; j_task < TASK_MAX_NUM; j_task++)//排队执行可执行函数
{// 任务调用
if(TaskCtrl[j_task].Task_Running || TaskCtrl[j_task].Task_RealTime)
{
TaskCtrl[j_task].TaskHook();//执行函数
TaskCtrl[j_task].Task_Running = false;//执行结束
}
}
}
sigLEDk sigLED;
void AppInit(void)
{
sigLED.LED01=1;
sigLED.LED02=2;
sigLED.LED03=3;
sigLED.LED04=4;
}
void taskTest(void)
{
static unsigned int time;
for(unsigned char i=0;i<10;i++)
TxBuff[i]=0x00+i;
TxBuff[10]=0x0D;
TxBuff[11]=0x0A;
if((TxEndFlag == 0)&(timen - time >= 1000))
{
time = timen;
PMJD_UART1_DMA_Send(TxBuff,12);
}
}
//实时性任务
void taskTestTime(void)
{
unsigned char TxBuffTest[BUFFER_SIZE];
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
15、“AppTask.h”的代码为
#ifndef _APPTASK_H_
#define _APPTASK_H_
#include "main.h"
#include "stdio.h"
#include "stdbool.h"
#include "adc.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "BspSigLED.h"
#define cycle_task 1 //每cycle_task个timen周期 轮换一次
typedef struct
{
bool Task_RealTime; // 任务是否需要实时调用
bool Task_Running; // 任务运行标记
unsigned short Task_Counter; // 任务计数器
unsigned short Task_PeriodTime; // 任务运行周期
void (*TaskHook)(void); // 任务函数指针
}TaskCtrl_t;
void App_task(void);
void taskTest(void);
void taskTestTime(void);
void AppInit(void);
#endif
16、烧入程序? 1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁; 0-常灭 ,观察闪烁周期
经过验证,和响应和预设状态相同
1-常亮; 2-T=200ms闪烁; 3-T=500ms闪烁; 4-T=1s闪烁;
灯光 成功
17、验证串口收发功能正常? 成功
?
?
|