一。软件定时器概念及应用
1.软件定时器定义
就是软件实现定时器。
2.FreeRTOS软件定时器介绍
如上图所示,Times的左边为设置定时器时间,设置方式可以为任务设置或者中断设置;Times的右边为定时器的定时响应,使用CallBack响应。
3.FreeRTOS软件定时器工作原理
软件定时器可以参考一下手机的闹钟。单次响应与多次响应。
二。软件定时器函数应用
1.功能需求
- 使用软件定时器功能完成闹钟功能设计
- 当闹钟到达时,可根据执行动作,触发相关的led亮灭
2.API
(1)xTimerCreate()创建一个定时器
(2)xTimerStart()启动定时器
(3) xTimerReset()重启软件定时器
(4)pvTimerGetTimerID()获取软件定时器标识符值
(5)xTimerChangePeriod()修改软件定时器周期值
3.功能设计
如上图所示,需要三个部分,用户在串口端设置时钟参数,RTC作为定时器的核心,并驱动GPIO
(1)串口命令定义
1.设置实时时钟 参数头:年-月-日,小时:分钟:秒 realtime:2019-2-19,16:31:00
2.设置闹钟参数 参数头:小时:分钟:秒,是否重复,操作LED动作 alarmtime:16:32:40,0,0
(2)功能业务划分
1.实时时钟:RTC功能开发
2.命令参数配置:串口解析功能开发
3.软件定时功能:软件定时器
4.多任务消息同步:消息队列
4.功能实现分析
(1)Cubemx配置
1.配置RTC
2.配置串口
3.创建任务
4.创建消息队列
(2)实时时钟读写操作
1.设置实时时钟
2.读取实时时钟
(3)命令解析任务
1.使能串口接收中断
2.串口中断发送消息队列
3.解析命令字符串
4.解析实时时钟字符串
5.解析闹钟字符串
6.计算闹钟与实时时钟间隔
(3)软件定时器回调函数
1.定时器打印实时时钟
2.闹钟回调函数
(4)LED处理任务
LED处理任务
5.功能实现详细步骤
1.Cubemx创建工程
注意:是在物联网操作系统第5节消息队列的基础之上创建工程。
(1)使能RCC低速时钟(LSE)
注意:上述在核心板原理图上
具体解释:
软件实现定时器,需要连接实时时钟,上述图片的晶振是操作实时时钟的晶振,这个晶振连接在低速时钟上,所以我们需要配置低速时钟。
(2)配置RTC
(3)时钟配置为低速的外部时钟
2.FREERTOS配置
(1)软件定时器的配置
(2) 创建控制Led的消息队列
(3)RTC时钟的创建
有一个定时打印的实时功能,所以创建一个RTC的时钟
6.步骤:
1.RTC配置
(1)RTC.h的设置
typedef struct{
RTC_TimeTypeDef RtcTime;
RTC_DateTypeDef RtcDate;
}RTCTimeDates;
void SetRTC(RTCTimeDates *pRTCTimeDate);
RTCTimeDates GetRTC(void);
(2)RTC.c的配置
void SetRTC(RTCTimeDates *pRTCTimeDate){
if (HAL_RTC_SetTime(&hrtc, &pRTCTimeDate->RtcTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
if (HAL_RTC_SetDate(&hrtc, &pRTCTimeDate->RtcDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}
RTCTimeDates GetRTC(void){
RTCTimeDates RTCTimeDate;
if (HAL_RTC_GetTime(&hrtc, &RTCTimeDate.RtcTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
if (HAL_RTC_GetDate(&hrtc, &RTCTimeDate.RtcDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
printf("Real Time:%d-%d-%d %d:%d:%d\n",
RTCTimeDate.RtcDate.Year + 2000,
RTCTimeDate.RtcDate.Month,
RTCTimeDate.RtcDate.Date,
RTCTimeDate.RtcTime.Hours,
RTCTimeDate.RtcTime.Minutes,
RTCTimeDate.RtcTime.Seconds
);
return RTCTimeDate;
}
2.FREERTOS配置
(1)FREERTOS.c加入使用到的头文件,创建使用的结构体
#include "rtc.h"
#include "stdlib.h"
typedef struct tmrTimerControl
{
const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */
TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */
UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */
void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */
#endif
} xTIMER;
(2)配置宏
#define LED_NUM 4
//串口接收buff
#define MESSAGE_BUFF_SIZE 50
//led接收buff
#define LED_MESSAGE_BUFF_SIZE 20
//实时时钟字符串头
#define REALTIME "realtime"
//闹钟字符串头
#define ALARMTIME "alarmtime"
//ms转换
#define ST0MS 1000ul
#define MT0MS (ST0MS*60)
#define HT0MS (MT0MS*60)
#define DT0MS (HT0MS*24)
(3)定义数据与字符串集,在命令分析时对比使用
uint8_t u8CmdBuff[MESSAGE_BUFF_SIZE];
static uint8_t u8LedMessageBuff[LED_MESSAGE_BUFF_SIZE];
//LED消息队列,字符串集
uint8_t *LedCmdString[LED_NUM*2] = {
"openled6",
"openled7",
"openled8",
"openled9",
"closeled6",
"closeled7",
"closeled8",
"closeled9",
};
uint8_t *OpenString[LED_NUM] = {
"openled6",
"openled7",
"openled8",
"openled9",
};
uint8_t *CloseString[LED_NUM] = {
"closeled6",
"closeled7",
"closeled8",
"closeled9",
};
GPIO_TypeDef * LedPort[LED_NUM] = {
Led6_GPIO_Port,
Led7_GPIO_Port,
Led8_GPIO_Port,
Led9_GPIO_Port
};
uint16_t LedPin[LED_NUM] ={
Led6_Pin,
Led7_Pin,
Led8_Pin,
Led9_Pin
};
//实时时钟字符串拆分解析结构体
struct sRealTimeString{
uint8_t Hours[10];
uint8_t Minutes[10];
uint8_t Seconds[10];
uint8_t Month[10];
uint8_t Date[10];
uint8_t Year[10];
}RealTimeString;
//闹钟字符串拆分解析结构体
struct sAlarmTimeString{
uint8_t Hours[10];
uint8_t Minutes[10];
uint8_t Seconds[10];
uint8_t Mode[10];
uint8_t Action[10];
}AlarmTimeString;
//闹钟参数结构体
typedef struct{
uint8_t Hours;
uint8_t Minutes;
uint8_t Seconds;
uint8_t Mode;
uint8_t Action;
} sAlarmTime;
(4)led的任务中分析命令与开关灯的函数
void vLedParseString(uint8_t *buff){
uint8_t i;
for(i=0;i<LED_NUM;i++){
if(strcmp((char const*)buff,(char const*)OpenString[i]) == 0){
HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_RESET);
printf("Cmd is %s\n",OpenString[i]);
return;
}
}
for(i=0;i<LED_NUM;i++){
if(strcmp((char const*)buff,(char const*)CloseString[i]) == 0){
HAL_GPIO_WritePin(LedPort[i], LedPin[i], GPIO_PIN_SET);
printf("Cmd is %s\n",CloseString[i]);
return;
}
}
}
(5)LedTask主函数的内容
uint8_t u8Index;
osTimerStart(RtcTimerHandle,1000);//注意与自己写的东西相同
/* Infinite loop */
for(;;)
{
//每次读取消息之前,把索引初始化为0
u8Index = 0;
//1、一直等待接收消息,第一个消息应该放在消息缓冲区的第一个元素上
if(xQueueReceive(LedQueueHandle,&u8LedMessageBuff[u8Index++],portMAX_DELAY)==pdPASS){
while(xQueueReceive(LedQueueHandle,&u8LedMessageBuff[u8Index++],50)){}
u8LedMessageBuff[u8Index] = '\0';//保证一包完整字符串信息
vLedParseString(u8LedMessageBuff);
//完成解析以后,要清空接收缓冲区,不然会出现问题
memset(u8LedMessageBuff,0,MESSAGE_BUFF_SIZE);
}
}
(6)在串口的任务上方,写函数触发的函数
//闹钟触发的软件定时器,回调函数
void vTimerCallback(xTimerHandle pxTimer){
uint32_t ulTimerID;
uint8_t i;
//获取当前定时器ID
ulTimerID = (uint32_t)pvTimerGetTimerID(pxTimer);
//判断定时器工作模式,如果自动重载,则更新软件定时器周期
if(((xTIMER*)pxTimer)->uxAutoReload){
xTimerChangePeriodFromISR(pxTimer,DT0MS,NULL);
printf("明天继续触发动作!!!\r\n");
}
printf("ulTimerID = %d\r\n",ulTimerID);
//根据软件定时器ID号, 发送到led消息队列中
for(i=0;i<strlen((char const*)LedCmdString[ulTimerID]);i++){
xQueueSend(LedQueueHandle,&LedCmdString[ulTimerID][i],1);
}
}
//计算闹钟与实时时钟之前的间隔时间,返回ms
uint32_t CountAlarmInterval(sAlarmTime AlarmTime){
int32_t AlarmTimeTick,RealTimeTick;
RTCTimeDates RTCTimeDate;
//获取实时时钟
RTCTimeDate = GetRTC();
//计算闹钟ms计数
AlarmTimeTick = AlarmTime.Hours*HT0MS+AlarmTime.Minutes*MT0MS+AlarmTime.Seconds*ST0MS;
//计算实时时钟ms计数
RealTimeTick = RTCTimeDate.RtcTime.Hours*HT0MS+RTCTimeDate.RtcTime.Minutes*MT0MS+RTCTimeDate.RtcTime.Seconds*ST0MS;
printf("AlarmTimeTick = %lu\r\n",AlarmTimeTick);
printf("RealTimeTick = %lu\r\n",RealTimeTick);
//判断闹钟是否大于等于当前实时时钟
//大于->返回闹钟-实时时钟
if((AlarmTimeTick-RealTimeTick) >= 0){
return AlarmTimeTick-RealTimeTick;
}else{
//小于->一天的ms值+实时时钟-返回闹钟
return DT0MS+RealTimeTick-AlarmTimeTick;
}
}
void ParseAlarmTimeString(uint8_t *buff){
char *pbufftime;
char *pbufftimeindex;
char *pbuffparm;
char *pbuffparmindex;
uint32_t AlarmTick;
TimerHandle_t xTimer;
sAlarmTime AlarmTime;
void SetRTC(RTCTimeDates *pRTCTimeDate);
//获取闹钟时间字符串指针
pbufftime = strstr((char const *)buff, ":");
//获取闹钟参数字符串指针
pbuffparm = strstr((char const *)buff, ",");
if (pbufftime != NULL)
{
//指针加1 取出正确的头指针
pbufftime++;
//取出正确的尾指针
pbufftime = strtok(pbufftime, ",");
//取出小时
pbufftimeindex = strtok(pbufftime, ":");
memcpy(AlarmTimeString.Hours, pbufftimeindex, strlen(pbufftimeindex));
//取出分钟
pbufftimeindex = strtok(NULL, ":");
memcpy(AlarmTimeString.Minutes, pbufftimeindex, strlen(pbufftimeindex));
//取出秒
pbufftimeindex = strtok(NULL, ":");
memcpy(AlarmTimeString.Seconds, pbufftimeindex, strlen(pbufftimeindex));
}
if (pbuffparm != NULL)
{
//指针加1 取出正确的头指针
pbuffparm++;
//取出工作模式
pbuffparmindex = strtok(pbuffparm, ",");
memcpy(AlarmTimeString.Mode, pbuffparmindex, strlen(pbuffparmindex));
//取出执行动作
pbuffparmindex = strtok(NULL, ",");
memcpy(AlarmTimeString.Action, pbuffparmindex, strlen(pbuffparmindex));
}
printf("设置闹钟系统时间为:%s:%s:%s\r\n",
AlarmTimeString.Hours,
AlarmTimeString.Minutes,
AlarmTimeString.Seconds);
printf("设置闹钟工作模式为:%s\r\n",
AlarmTimeString.Mode);
printf("设置闹钟执行动作为:%s\r\n",
AlarmTimeString.Action);
//转换字符串格式的闹钟参数为整型值
AlarmTime.Hours = atoi((char const *)AlarmTimeString.Hours);
AlarmTime.Minutes = atoi((char const *)AlarmTimeString.Minutes);
AlarmTime.Seconds = atoi((char const *)AlarmTimeString.Seconds);
AlarmTime.Mode = atoi((char const *)AlarmTimeString.Mode);
AlarmTime.Action = atoi((char const *)AlarmTimeString.Action);
//计数周期间隔
AlarmTick = CountAlarmInterval(AlarmTime);
printf("当前闹钟间隔为:%lu\r\n",AlarmTick);
//创建定时器,传入间隔、工作模式、触发动作
xTimer = xTimerCreate("timer",AlarmTick,AlarmTime.Mode,(void*)AlarmTime.Action,vTimerCallback);
//判断定时器是否创建成功
if(xTimer != NULL){
//启动定时器
xTimerStart(xTimer,0);
printf("启动定时器成功!\r\n");
}
}
//解析实时时钟字符串
void ParseRealTimeString(uint8_t *buff)
{
char *pbuffdate;
char *pbuffdateindex;
char *pbufftime;
char *pbufftimeindex;
RTCTimeDates RTCTimeDate;
//获取日期字符串指针
pbuffdate = strstr((char const *)buff, ":");
//获取时间字符串指针
pbufftime = strstr((char const *)buff, ",");
if (pbuffdate != NULL)
{
//指针加1 取出正确的头指针
pbuffdate++;
//取出正确的尾指针
pbuffdate = strtok(pbuffdate, ",");
//取出年
pbuffdateindex = strtok(pbuffdate, "-");
memcpy(RealTimeString.Year, pbuffdateindex, strlen(pbuffdateindex));
//取出月
pbuffdateindex = strtok(NULL, "-");
memcpy(RealTimeString.Month, pbuffdateindex, strlen(pbuffdateindex));
//取出天
pbuffdateindex = strtok(NULL, "-");
memcpy(RealTimeString.Date, pbuffdateindex, strlen(pbuffdateindex));
}
if (pbufftime != NULL)
{
//指针加1 取出正确的头指针
pbufftime++;
//取出小时
pbufftimeindex = strtok(pbufftime, ":");
memcpy(RealTimeString.Hours, pbufftimeindex, strlen(pbufftimeindex));
//取出分钟
pbufftimeindex = strtok(NULL, ":");
memcpy(RealTimeString.Minutes, pbufftimeindex, strlen(pbufftimeindex));
//取出秒
pbufftimeindex = strtok(NULL, ":");
memcpy(RealTimeString.Seconds, pbufftimeindex, strlen(pbufftimeindex));
}
printf("设置当前系统时间为:%s-%s-%s,%s:%s:%s\r\n",
RealTimeString.Year,
RealTimeString.Month,
RealTimeString.Date,
RealTimeString.Hours,
RealTimeString.Minutes,
RealTimeString.Seconds);
//字符串转换为实时时钟值
RTCTimeDate.RtcDate.Year = atoi((char const *)RealTimeString.Year) - 2000;
RTCTimeDate.RtcDate.Month = atoi((char const *)RealTimeString.Month);
RTCTimeDate.RtcDate.Date = atoi((char const *)RealTimeString.Date);
RTCTimeDate.RtcTime.Hours = atoi((char const *)RealTimeString.Hours);
RTCTimeDate.RtcTime.Minutes = atoi((char const *)RealTimeString.Minutes);
RTCTimeDate.RtcTime.Seconds = atoi((char const *)RealTimeString.Seconds);
//设置当前实时时钟
SetRTC(&RTCTimeDate);
}
//解析串口命令字符串
void vCmdParseString(uint8_t *buff){
//判断是否为实时时钟设置
if(strncmp((char const*)buff,REALTIME,strlen(REALTIME)) == 0){
ParseRealTimeString(buff);
}
//判断是否为闹钟设置
else if(strncmp((char const*)buff,ALARMTIME,strlen(ALARMTIME)) == 0){
ParseAlarmTimeString(buff);
}
}
(7)UsartTask任务中编写————》注意接受缓冲区要清除多少
uint8_t u8Index;
/* Infinite loop */
for(;;)
{
//每次读取消息之前,把索引初始化为0
u8Index = 0;
//1、一直等待接收消息,第一个消息应该放在消息缓冲区的第一个元素上
if(xQueueReceive(CmdQueueHandle,&u8CmdBuff[u8Index++],portMAX_DELAY)==pdPASS){
while(xQueueReceive(CmdQueueHandle,&u8CmdBuff[u8Index++],50)){}
u8CmdBuff[u8Index] = '\0';//保证一包完整字符串信息
vCmdParseString(u8CmdBuff);
//完成解析以后,要清空接收缓冲区,不然会出现问题
memset(u8CmdBuff,0,MESSAGE_BUFF_SIZE);
}
}
结果:
文章来源:https://www.toymoban.com/news/detail-659031.html
文章来源地址https://www.toymoban.com/news/detail-659031.html
到了这里,关于9.物联网操作系统之软件定时器,实现一个闹钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!