前言:记录个人学习使用STMCubeMx软件,通过freeRTOS队列功能进行OLED显示和RTC两个线程任务之间的结构体数据传输、RTC定时器闹钟中断回调函数释放中断二值信号实现相应任务退出阻塞状态、和任务之间的状态控制。
创建基本工程框架
1、配置I2C(硬件配置)
2、配置GPIO口(自动生成)
二、RTC配置
1、配置RTC定时器(激活时钟源、激活日历)
2、自定义初始化时钟和日期信息
3、配置时钟源(外部32M时钟源)
4、激活RTC定时器周期唤醒中断和RTC定时器闹钟中断
三、创建任务
1、可复写Oled显示任务
2、可复写RTC任务
3、可复写RTC闹钟任务
四、创建队列(uint32_t类型)存放RTC相应结构体
五、创建二值信号
六、参考代码
1、main.c
(1)添加stdio头文件(测试板子复位后往串口发送数据,PC端是否成功接收)
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
(2)复位上电:测试串口发送功能
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
printf("successful!\r\n");
/* USER CODE END 2 */
/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2、freertos.c
(1)添加左侧项目栏rtc.c的头文件rtc.h,和stdio头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "rtc.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
(2)修改创建二值信号代码
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* Create the semaphores(s) */
/* definition and creation of myBinary */
//osSemaphoreDef(myBinary);
//myBinaryHandle = osSemaphoreCreate(osSemaphore(myBinary), 1);
//手动创建二值信号(FreeRTOS系统自动创建会自带释放二值信号)
myBinaryHandle = xSemaphoreCreateBinary();
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
(3)cubeMx自动生成了三个线程
3、Clock.c代码(手动创建.c文件并导入项目中)
RTC任务:
(1)任务初始化自定义数据配置RTC定时器闹钟
(2)没隔1s(阻塞状态延时1s)进行对RTC定时器数据进行读取,并通过队列已RTC数据储存结构体传输给OLED显示任务
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "rtc.h"
#include "stdio.h"
/*freertos.c文件句柄*/
extern osMessageQId Queue01Handle;
//RTC设置闹钟
void RTC_Set_Alarm(uint8_t *Set_Alarm_Data)
{
RTC_AlarmTypeDef rtc_alarmtypedef = {0};
//设置时间
rtc_alarmtypedef.Alarm = RTC_ALARM_A;
rtc_alarmtypedef.AlarmTime.Hours = Set_Alarm_Data[0];
rtc_alarmtypedef.AlarmTime.Minutes = Set_Alarm_Data[1];
rtc_alarmtypedef.AlarmTime.Seconds = Set_Alarm_Data[2];
//设置RTC中断
HAL_RTC_SetAlarm_IT(&hrtc, &rtc_alarmtypedef, RTC_FORMAT_BIN);
//等待RTC寄存器(RTC_CRL)低5位RTOFF,若RTC寄存器的写操作仍在进行,处于低电平(0)
while(!__HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_FLAG_RTOFF));
}
//获取RTC数据
void RTC_Get_Time(void)
{
//获取日期结构体
RTC_DateTypeDef GetData;
//获取时间结构体
RTC_TimeTypeDef GetTime;
/* 获取当前实时时间 */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* 获取当前实时日期 */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/*队列发送获取结构体数据*/
xQueueSend(Queue01Handle, &GetData, portMAX_DELAY);
xQueueSend(Queue01Handle, &GetTime, portMAX_DELAY);
}
//复写RTC时钟任务
void StartTaskClock(void const * argument)
{
/* USER CODE BEGIN StartTaskClock */
//闹钟数据定义
uint8_t Alarm_Data[3] = {19, 40, 05};
//设置闹钟
RTC_Set_Alarm(Alarm_Data);
/* Infinite loop */
for(;;)
{
//获取RTC数据
RTC_Get_Time();
osDelay(1000);
}
/* USER CODE END StartTaskClock */
}
4、rtc.c(在最底下USER CODE BEGIN 1注释内 复写RTC闹钟中断回调函数)
/* USER CODE BEGIN 1 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
BaseType_t pxHigherPriorityTaskWoken;
//恢复Oled闹钟显示任务
osThreadResume(TaskOled_AlarmHandle);
//中断二值信号量释放
if (xSemaphoreGiveFromISR(myBinaryHandle, &pxHigherPriorityTaskWoken) == pdFAIL)
{
printf("Semaphore Give Fail From ISR!\r\n");
}
//printf("clock over! .... ....\r\n");
}
/* USER CODE END 1 */
5、Oled.c代码(手动创建.c文件并导入项目中)
Oled显示任务:
实时获取队列传输的RTC结构体数据并通过OLED模块显示
OLED时钟任务:
(1)任务初始化挂起,处于挂起状态
(2)若RTC触发中断,同时RTC释放中断二值信号,则恢复任务状态进入就绪态,同时并进行获取中断二值信号阻塞等待。
(3)若成功获取中断二值信号,并挂起Oled显示任务状态,实行OLED时钟任务内实现效果的代码。
(4)运行相应代码后,恢复Oled显示任务状态并且挂起自身OLED时钟任务状态。
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "font.h"
#include "i2c.h"
#include "stdio.h"
/*freertos.c文件句柄*/
extern osMessageQId Queue01Handle;
extern osSemaphoreId myBinaryHandle;
extern osThreadId TaskOled_ShowHandle;
extern osThreadId TaskOled_AlarmHandle;
//Oled初始化指令
uint8_t Oled_Init_CMD[] =
{
0xAE,0xD5,0x80,0xA8,0x3F,0xD3,0x00,0x40,0xA1,
0xC8,0xDA,0x12,0x81,0xCF,0xD9,0xF1,0xDB,0x30,
0xA4,0xA6,0x8D,0x14,0xAF
};
//I2c写指令
void I2c_Write_Cmd(uint8_t cmd)
{
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, portMAX_DELAY);
}
//I2c写数据
void I2c_Write_Data(uint8_t cmd)
{
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &cmd, 1, portMAX_DELAY);
}
//Oled初始化设置
void Oled_Start_Init(void)
{
uint8_t i = 0;
for(i = 0; i < 23; i++)
{
I2c_Write_Cmd(Oled_Init_CMD[i]);
}
}
//Oled显示位置
void Oled_Display_Position(uint8_t Page, uint8_t Seg)
{
//配置Oled显示的Page
I2c_Write_Cmd(0xB0 + Page);
/*配置Oled显示的Seg*/
//配置显示Seg低八位
I2c_Write_Cmd((Seg % 0x0f) | 0x00);
//配置显示Seg高八位
I2c_Write_Cmd((Seg % 0xf0) >> 4 | 0x10);
}
//Oled清屏
void Oled_Clear_Screen(void)
{
for(uint8_t i = 0; i<8; i++)
{
Oled_Display_Position(i, 0);
for(uint8_t j = 0; j<128; j++)
{
I2c_Write_Data(0x00);
}
}
}
//Oled显示字符
void Oled_Show_Char(uint8_t Page, uint8_t Seg, uint8_t Char, uint8_t Size)
{
//判断需要多少个Page
uint8_t page = Size/8;
if(Size % 8)
page++;
//根据font.h文件计算对应存储的位置
Char -= ' ';
for(uint8_t i = 0; i < page; i++)
{
Oled_Display_Position(i + Page, Seg);
for(uint8_t j = i * Size / 2; j < (i + 1) * Size / 2; j++)
{
if(Size == 16)
{
I2c_Write_Data(ascii_8X16[Char][j]);
}
}
}
}
//Oled显示字符串
void Oled_Show_String(uint8_t Page, uint8_t Seg, uint8_t *String, uint8_t Size)
{
while(*String != '\0')
{
Oled_Show_Char(Page, Seg, *String++, Size);
//对SEG列进行尺寸一半偏右移位输出下一个字符
Seg += Size / 2;
}
}
//Oled显示汉字
void Oled_Show_Chinese(uint8_t Page, uint8_t Seg, uint8_t Number, uint8_t Size)
{
for(uint8_t i = 0; i < Size / 8; i++)
{
Oled_Display_Position(i + Page, Seg);
for(uint8_t j = i * Size; j < (i + 1) * Size; j++)
{
if(Size == 16)
{
I2c_Write_Data(chinese_16x16[Number][j]);
}
}
}
}
//Oled显示图片
void Oled_Show_Image(uint8_t Page, uint8_t Seg, uint8_t width, uint8_t heigth, uint8_t *image)
{
for(uint8_t i = 0; i < heigth / 8; i++)
{
Oled_Display_Position(i + Page, Seg);
for(uint8_t j = 0; j < width; j++)
{
I2c_Write_Data(*image++);
}
}
}
//Oled显示
void Oled_Display(RTC_DateTypeDef *GetData, RTC_TimeTypeDef *GetTime)
{
Oled_Show_String(0,0, "20", 16);
Oled_Show_Char(0, 16, GetData->Year/10 + '0', 16);
Oled_Show_Char(0, 24, GetData->Year%10 + '0', 16);
Oled_Show_Char(0, 32, '/', 16);
Oled_Show_Char(0, 40, GetData->Month/10 + '0', 16);
Oled_Show_Char(0, 48, GetData->Month%10 + '0', 16);
Oled_Show_Char(0, 56, '/', 16);
Oled_Show_Char(0, 64, GetData->Date/10 + '0', 16);
Oled_Show_Char(0, 72, GetData->Date%10 + '0', 16);
Oled_Show_Char(2, 0, GetTime->Hours/10 + '0', 16);
Oled_Show_Char(2, 8, GetTime->Hours%10 + '0', 16);
Oled_Show_Char(2, 16, ':', 16);
Oled_Show_Char(2, 24, GetTime->Minutes/10 + '0', 16);
Oled_Show_Char(2, 32, GetTime->Minutes%10 + '0', 16);
Oled_Show_Char(2, 40, ':', 16);
Oled_Show_Char(2, 48, GetTime->Seconds/10 + '0', 16);
Oled_Show_Char(2, 56, GetTime->Seconds%10 + '0', 16);
}
//复写Oled显示任务
void StartTaskOled_Show(void const * argument)
{
/* USER CODE BEGIN StartTaskOled */
//获取日期结构体
RTC_DateTypeDef GetData;
//获取时间结构体
RTC_TimeTypeDef GetTime;
//Oled初始化设置
Oled_Start_Init();
//Oled清屏
Oled_Clear_Screen();
/* Infinite loop */
for(;;)
{
/*队列接收获取结构体数据*/
xQueueReceive(Queue01Handle, &GetData, portMAX_DELAY);
xQueueReceive(Queue01Handle, &GetTime, portMAX_DELAY);
//Oled显示
Oled_Display(&GetData, &GetTime);
osDelay(1);
}
/* USER CODE END StartTaskOled */
}
//复写Oled闹钟显示任务
void StartTaskOled_Alarm(void const * argument)
{
/* USER CODE BEGIN StartTaskOled_Alarm */
//指向BaseType_t的指针,用于指示是否有更高优先级的任务被唤醒。
BaseType_t pxHigherPriorityTaskWoken;
//暂停Oled闹钟显示任务
osThreadSuspend(TaskOled_AlarmHandle);
/* Infinite loop */
for(;;)
{
//获取中断二值信号
if (xSemaphoreTakeFromISR(myBinaryHandle, &pxHigherPriorityTaskWoken ) == pdTRUE)
{
//挂起Oled显示任务
osThreadSuspend(TaskOled_ShowHandle);
//Oled清屏
Oled_Clear_Screen();
//Oled显示图片
Oled_Show_Image(0, 0, 128, 61, (unsigned char*)Image[0]);
//阻塞2秒(实现图片显示2s)
osDelay(2000);
//Oled清屏
Oled_Clear_Screen();
//恢复Oled显示任务
osThreadResume(TaskOled_ShowHandle);
//暂停Oled闹钟显示任务
osThreadSuspend(TaskOled_AlarmHandle);
}
else
{
printf("Semaphore Task Fail From ISR!\r\n");
}
osDelay(1);
}
/* USER CODE END StartTaskOled_Alarm */
}