蓝桥杯嵌入式B站课程总结

249 阅读18分钟

〇、新建工程

①打开STM32Cube,点击中间部分的“Access To MCU SELECTOR”;
image.png

②搜索“STM32G431RBT6” 1737363026586.png

image.png ③“System Core”中点“RCC”,“High Speed Clock(HSE)”选择“Crystal/Ceramic Resonator”(外部时钟使能),“SYS”中“Debug”选择“Serial Wire”;
④时钟配置输入24MHz,选择“HSE”,选择“PLLCLK”,“HCLK(MHz)”选择“80MHz”回车;

image.png 最终: image.png ⑤设置引脚,包括看GPIO默认输出状态(eg.例如设置GPIO output level为High); image.png

⑥点“Project Manager”中,“Project”的“Project Name”设置名字,“Toolchain/IDE”设置“MDK-ARM” “Min Version” 设置版本(eg.本机为“v5”),下方“Use Default Firmware Location”勾选取消,点击最后方三个点选择库文件打开; 1737364860849.png

⑦“Code Generator”中“Generated files”的第一个“Generate peripheral initialzation as a pair of ...”,点击“GENERATE CODE”生成代码打开; image.png

image.png

一、点亮LED

image.png

step1: PD2输出高电平(PD2为使能端)

step2: PC8~PC15输出低电平,即点亮

step3: PD2输出低电平

注意: image.png

PS: 下载要注意去“魔术棒”那里设置一下,“Debug”的右半边“Use”选择“CMSIS-DAP Debugger”

image.png

再勾选“Reset and Run”

image.png

.h文件最后留一行可以避免Waring

image.png

void led_show(uint8_t led,uint8_t mode)
{
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	if(mode)//mode==1,置低电平,即点亮
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (led - 1),GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8 << (led - 1),GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}


二、按键

image.png 属于上拉输入,即按键没有按下时输入得到高电平,按键按下时,输入得到低电平;

uint8_t led1_state=0;
void key_scan()
{
	B1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
	if(B1_state == 0 && B1_last_state ==1)//按键S1按下
	{
		if(led1_state)
			led1_state=0;
		else
			led1_state=1;
		led_show(1,led1_state);
		
	}
	B1_last_state = B1_state;	
}

三、LCD


重要:解决lcd与led引脚冲突问题

image.png

1737558807985.png 这些lcd函数改变了pc端的引脚的一个输出,一开始配置的时候将PD2置成高电平,是为了让初始化的时候就让led的一端即led的负端置高电平,默认一个熄灭的状态---》由此我们可以在lcd初始化之前让PD2置低电平
(即加上: HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);) 这样lcd就不会去改变led的状态
然后再到 LCD_DisplayStringLine(u8 Line, u8 *ptr) 函数中增加一句:

uint16_t temp = GPIOC->ODR;

就是在执行这个函数之前先把PC端这个输出状态全部存到这个temp变量里面;
再在结束之后将它的输出状态给它复原:

	GPIOC->ODR = temp;

四、LED闪烁

1、原理:定时器中断

image.png 在Cube里选择TIM2(即通用定时器,TIM1是高级定时器

image.png (PSC是预分频器,ARR是自动重装载值,CNT是计时器image.png PS: PSC不能超过2的16次方即65535
再点“NMC Settings”,选择使能Enable打勾);


五、长按键和短按键

image.png

image.png

1737876686395.png ①TIM3中Clock Source选择“Internal Clock”(内部时钟)
②PSC设置为8000-1,ARR直接设置为最大值即可(65535) image.png

	if(B1_state == 0 && B1_last_state ==1)//按键B1按下
	{
		TIM3->CNT = 0;
	}
	else if(B1_state == 0 && B1_last_state == 0)//当按键B1一直按着
	{
		if(TIM3->CNT >= 10000)//按键B1长按
		{
			count++;
		}
	}
	else if(B1_state == 1 && B1_last_state == 0)//当按键B1松开
	{
		if(TIM3->CNT < 10000)
		{
			count += 2;
		}
	}
        

PS:在main函数中记得使能tim3

  HAL_TIM_Base_Start(&htim3);//使能

六、lcd高亮显示

①首先定义一个变量uint8_t lcd_highshow;
②在按下某个按钮的时候lcd_highshow++,然后lcd_highshow对3取余(因为有三种选项可以高亮),确保lcd_highshow在0,1,2循环;

uint8_t lcd_highshow;
    if(B4_state == 0 && B4_last_state ==1)//按键B4按下
    {
            lcd_highshow++;
            lcd_highshow %= 3;
            led_show(2, 0);
    }
    void lcd_show()
{
	sprintf(text,"        text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	if(lcd_highshow == 0)
	{
		LCD_SetBackColor(Yellow);//高亮显示
		sprintf(text,"         count:%d       ",count);
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
		LCD_SetBackColor(Black);
		sprintf(text,"         pare1          ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
		sprintf(text,"         pare2          ");
		LCD_DisplayStringLine(Line5,(uint8_t *)text);
	}
	else if(lcd_highshow == 1)
	{	
		sprintf(text,"         count:%d       ",count);
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
		LCD_SetBackColor(Yellow);//高亮显示
		sprintf(text,"         pare1          ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
		LCD_SetBackColor(Black);
		sprintf(text,"         pare2          ");
		LCD_DisplayStringLine(Line5,(uint8_t *)text);
	}
	else if(lcd_highshow == 2)
	{	
		sprintf(text,"         count:%d       ",count);
		LCD_DisplayStringLine(Line3,(uint8_t *)text);
		sprintf(text,"         pare1          ");
		LCD_DisplayStringLine(Line4,(uint8_t *)text);
		LCD_SetBackColor(Yellow);//高亮显示
		sprintf(text,"         pare2          ");
		LCD_DisplayStringLine(Line5,(uint8_t *)text);
		LCD_SetBackColor(Black);
	}
	led_show(1,led_mode);
}

七、PWM输出

①打开Cube创建完工程,然后将引脚PA1设置为TIM2_CH2;
②Timers中选择TIM2,再在TIM2中Channel2选择“PWM Generation CH2” image.png ③计算当输出频率为1000Hz时的ARR值与PSC值;
image.png ④修改ARR与PSC值,生成代码;
设置频率
⑤打开keil文件,点开tim.c前面的"+"号(如果没有出现加号,则可以先编译一下),然后找到“stm32g4xx_hal_tim.h”打开,找到HAL_TIM_PWM_Start(大概在2400多行)将其复制粘贴到main函数的可输入代码段中,参数选择&htim2TIM_CHANNEL_2(可以去函数的定义找);

  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

设置占空比
⑥计算占空比 image.png ⑦在main函数中写入TIM2->CCR2 = 50通道2对应CCR2,CRR2 = 50 即 占空比为 50%
⑧CCR全称叫输出比较寄存器 image.png 当CCR>CNT时,PA1输出;当CCR<CNT时,PA1输出0;

image.png 占空比=0.5s/1s=50%=CCR*t/(ARR+1)*t=CCR/(ARR+1)


八、输入捕获量测量引脚输出PWM频率

1、

image.png caoture_value为捕获值
image.png

image.png

image.png

2、配置PA7引脚完成脉冲捕获功能

TIM17 CH1TIM3 CH2都可以实现,这里选择TIM17 CH1
image.png ②选择TIM17,勾选“Activated”,Channel选择“Input Capture direct mode”(即输入捕获image.png ③把PSC设置成80-1,在点击“NMC Settings”选择使能(PS:预分频的大小要介于可以测量高频率和低频信号不会溢出;捕获定时器的频率至少要高于输入信号的频率) image.png ④把test1中的code文件夹copy过来(记得添加路径) ⑤在tim.h中的stm32g4xx_hal_tim.h文件中找到“ HAL_StatusTypeDef HAL_TIM_IC_Start_IT**复制,并粘贴在main函数中;

    HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);
    //因为我们设置PA1为TIM17,CH1的捕获输入

⑥在stm32g4xx_hal_tim.h文件中找到HAL_TIM_IC_CaptureCallback(输入捕获回调函数),在fun.c中写输入捕获测量的中断函数
PS: 在写到if(htim->Instance == TIM17) 时去stm32g4xx_hal_tim.hctrl+F 查找函数readcapture(获取捕获值的函数)

    uint32_t fre,capture_value;//fre为频率,capture_value为捕获值
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM17)
	{
		capture_value = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
		TIM17->CNT = 0;//将计数器清零
		fre = 80000000/(80*capture_value);//测量频率
	}
}

⑦写lcd_show
fun.c文件中写入:

uint32_t fre,capture_value;//fre为频率,capture_value为捕获值
char text[20];
void lcd_show()
{
	sprintf(text,"        text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	sprintf(text,"        fre:%d        ",fre);
	LCD_DisplayStringLine(Line2,(uint8_t *)text);
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM17)
	{
		//不用调用函数的另一种写法:capture_value = TIM17->CCR1;
		capture_value = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//中断时获取一个捕获值
		//去ReadCheaper函数的定义中会发现其实读取CCR的值,这是因为在捕获到上升沿的时候会将CNT赋值给CCR
		TIM17->CNT = 0;//将计数器清零
		fre = 80000000/(80*capture_value);//测量频率

	}
}

PS:去ReadCheaper函数的定义中会发现其实读取CCR的值,这是因为在捕获到上升沿的时候会将CNT赋值给CCR,故而也可以直接写capture_value = TIM17->CCR1;

  LCD_Init();
  LCD_Clear(Black);
  LCD_SetBackColor(Black);
  LCD_SetTextColor(White);

九、输入捕获测555定时器频率

①配置一个test03_input_capture,芯片图基本配置(PA14配置“SYS_JTCK—SWCLK”,PA13配置“SYS_JTMS—SWDIO”,PF0配置“RCC_OSC_IN”,PF1配置“RCC_OSC_OUT”);
②PA15配置“TIM2_CH1”;
③PB4配置“TIM16_CH1”;
④Timers中“TIM2”选择“Channel1”中的“Input Capture direct mode”(输入捕获直接模式) image.pngPSC设置成80-1 image.png ⑥“NVIC Settings”使能(选择“Enable”)

image.png ⑦TIM16勾选一下“Activated”,Channel再选择“Input Capture direct mode”,PSC设置成80-1,生成文件

image.png ⑧打开C文件,把code文件添加一下(包括文件路径,Debug等等) 在main函数中输入一下代码(用于使能):

  /* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
  HAL_TIM_IC_Start_IT(&htim16,TIM_CHANNEL_1);
  /* USER CODE END 2 */

⑨在fun.c函数中写HAL_TIM_IC_CaptureCallback(频率输出函数)和lcd_show

uint32_t fre1,capture_value1,fre2,capture_value2;//fre为频率,capture_value为捕获值
char text[20];
void lcd_show()
{
	sprintf(text,"        text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	sprintf(text,"     R39_fre1:%d    ",fre1);
	LCD_DisplayStringLine(Line2,(uint8_t *)text);
	sprintf(text,"     R40_fre2:%d    ",fre2);
	LCD_DisplayStringLine(Line4,(uint8_t *)text);

	
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM16)//频率输出1  R39
	{//
		//不用调用函数的另一种写法:capture_value = TIM16->CCR1;
		capture_value1 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//中断时获取一个捕获值
		//去ReadCheaper函数的定义中会发现其实读取CCR的值,这是因为在捕获到上升沿的时候会将CNT赋值给CCR
		TIM16->CNT = 0;//将计数器清零
		fre1 = 80000000/(80*capture_value1);//测量频率
	}
	if(htim->Instance == TIM2)//频率输出2  R40
	{
		//不用调用函数的另一种写法:capture_value = TIM2->CCR1;
		capture_value2 = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//中断时获取一个捕获值
		//去ReadCheaper函数的定义中会发现其实读取CCR的值,这是因为在捕获到上升沿的时候会将CNT赋值给CCR
		TIM2->CNT = 0;//将计数器清零
		fre2 = 80000000/(80*capture_value2);//测量频率
	}
}

main函数LCD的初始化,以及lcd_show的写入;

十、adc测量

image.png 即通过设置PB15PB12ADC输入模式去测量R37与R37R38的电压值,可通过调整可调电位器R37与R38去改变测量到的电压值
①创建一个工程test04_adc,完成基本配置;
②STM32Cube配置:
aPB12设置为ADC1_IN11,PB15设置为ADC2_IN15,然后在左侧找到“Analog”,ADC1中找到“IN11”并设置为“IN11 Single-ended”;ADC2中找到“IN15 Single-ended”并打上勾,生成代码;
③将之前的code复制到工程中,在adc.c的下拉菜单中找到“stm32g4xx_hal_adc.h”,找到函数HAL_ADC_GetValue和函数HAL_ADC_Start(ADC输入模式开启),并在fun.c中编写lcd_show

char text[20];
void lcd_show()
{
	sprintf(text,"        text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	
	HAL_ADC_Start(&hadc1);
	uint32_t adc_value = HAL_ADC_GetValue(&hadc1);//R38

	sprintf(text,"      value:%d       ",adc_value);   //显示adc值
	LCD_DisplayStringLine(Line3,(uint8_t *)text);
}

④在main中完成LCD的初始化,并在while(1)中使用lcd_show();
⑤编写电压测量函数:

double get_vol(ADC_HandleTypeDef *hadc)//电压测量函数,参数表中的hadc如果是hadc1就是对应R38,如果是hadc2就是对应R37
{
	HAL_ADC_Start(hadc);
	uint32_t adc_value = HAL_ADC_GetValue(hadc);
	return 3.3*adc_value/4096;//电压计算公式
}

PS:电压计算公式为上述的原因:由模拟输入的电路图可知R37上的电压值为0~3.3V,读到的ad值是0 ~ 4096,故电压测量计算公式为 adc_value*3.3 / 4096,而为什么是4096呢,这是因为在ADC的“Resolution”为 ADC 12 bit resolution(从Cube中可以看),2的12次方就等于4096;
image.png lcd_show函数可以改为:

void lcd_show()
{
	sprintf(text,"        text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	
	sprintf(text,"   R37_VOL:%.2fV       ",get_vol(&hadc2));   //显示adc值
	LCD_DisplayStringLine(Line3,(uint8_t *)text);
	
	sprintf(text,"   R38_VOL:%.2fV       ",get_vol(&hadc1));   //显示adc值
	LCD_DisplayStringLine(Line5,(uint8_t *)text);
}


十一、串口发送和接收

根据数据手册 USART对应的引脚为PA9和PA10
①创建一个新工程teest05_usart,完成Cube基本设置,在左侧找到“Connectivity”,再其中的“USART1”中Mode选择“Asynchronous”(意为异步通信),可以注意到波特率是115200Bits/s,Gpio口的两个设置的是 PC4PC5,是错误的,这里我们需要重新设置为PA9和PA10,再把中断使能一下(“NVIC Setting”),生成代码;

【接收】

②编译一下,先去usart.c中找到“stm32gxx_hal_uart.h”,再在文件中找到HAL_UART_Transmit函数,在main函数中先写入头文件headfile.h,再在while()中写入一下代码:

char text01[20];
sprintf(text01,"Lan Qiao Bei\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)text01,sizeof(text01),50);//参数表依次为串口编号,文本,大小,时间
HAL_Delay(1000);//每隔1s发送一次

随后编译烧录;
打开嵌入式资源数据包,找到软件环境中的串口调试工具,然后设置端口(右键此电脑,找到管理,查看USB串行设备是哪个端口)以及波特率(要与Cube中USART设置的一样,一般是9600或115200),设置完成后打开串口就可以看到接收区每隔1s开发板通过串口给电脑发送的文本;

image.png

【发送】

③在“stm32gxx_hal_uart.h”中找到HAL_UART_RxCpltCallback(串口接收中断回调函数) PS:注意要将rec_data存到fun.h中: extern uint8_t rec_data

在fun.c的HAL_UART_RxCpltCallback函数中,把发送写在接收前面的逻辑:
由于在main函数的代码里写入了HAL_UART_Receive_IT(huart, &rec_data, 1);故会先执行接收再发送
接收-->发送-->接收;
也可以不在函数中写入HAL_UART_Receive_IT(huart, &rec_data, 1);将其写入main函数中的while循环里

uint8_t rec_data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
	if(huart->Instance == USART1)
	{
		HAL_UART_Transmit(huart,&rec_data,1,50);//参数表依次为串口编号,文本,大小,时间
		
		HAL_UART_Receive_IT(huart, &rec_data, 1);//参数表:串口句柄,接收的数据,接收数据字节数
		//函数里也可以不写HAL_UART_Receive_IT(huart, &rec_data, 1);直接放main中的while里
	}
}

十二、利用定时器进行串口不定长数据接收

以第12届真题为例: image.png

解题思路:
【1】
计算 image.png image.png 每次接收到数据CNT清零一次, 当t>1.04ms时,判断为没有下一个数据传入;
设置PSC+1=8000,
8000 / 80000000 = 1 / 10000,
CNT = 15 -> t = 15 * 1 / 10000 = 0.0015s = 1.5ms
image.png 设置比1.04要多0.4ms左右,这样更保险,所以可以用判断CNT是否大于15(也就是t是否大于15ms)来判断数据是否接收完成
Cube设置 a.将USART1的PSC+1(波特率)设置成9600 image.png b.打开一个时钟(本篇以TIM4为例)将Clock Source设置为“Internal Clock”生成代码;
c.在main函数中对时钟4进行使能HAL_TIM_Base_Start(&htim4);
d.编写接收函数uart_data_rec

char send_buff[20];//用来存储发送的字符串
uint8_t rec_data,count;//count为计数器,用于将接收到的数据依次存储进数组中
uint8_t rec_flag;//接收时置一
uint8_t rec_buff[20];//将接收到的数据存储进该数组
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
	if(huart->Instance == USART1)
	{
		//HAL_UART_Transmit(huart,&rec_data,1,50);//参数表依次为串口编号,文本,大小,时间
		TIM4->CNT = 0;//不要忘记计数器清零
		rec_flag = 1;
		rec_buff[count] = rec_data;
		count++;
		HAL_UART_Receive_IT(huart, &rec_data, 1);//参数表:串口句柄,接收的数据,接收数据字节数
		//函数里也可以不写HAL_UART_Receive_IT(huart, &rec_data, 1);直接放main中的while里
	}
}
void uart_data_rec()
{
	if(rec_flag)
	{
		if(TIM4->CNT > 15)   //数据接收完成
		{
			//处理数据
			if(count == 3 && rec_buff[0] == 'l'&& rec_buff[1]  == 'a'&& rec_buff[2] == 'n') //lan
			{
				sprintf(send_buff,"lan\r\n");//先将字符串存储进去
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);//串口发送			}				
			}
			else if(count == 4 && rec_buff[0] == 'q'&& rec_buff[1]  == 'i'&& rec_buff[2] == 'a'&& rec_buff[3] == 'o')//qiao
			{
				sprintf(send_buff,"qiao\r\n");//先将字符串存储进去
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);//串口发送			}				
			}
			else if(count == 3 && rec_buff[0] == 'b'&& rec_buff[1]  == 'e'&& rec_buff[2] == 'i')//bei
			{
				sprintf(send_buff,"bei\r\n");//先将字符串存储进去
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);//串口发送			}				
			}
			else
			{
				sprintf(send_buff,"error\r\n");//先将字符串存储进去
				HAL_UART_Transmit(&huart1,(uint8_t *)send_buff,sizeof(send_buff),50);//串口发送			}				
			}
			rec_flag = 0;
			for(int i=0;i<count;i++)//清除数组
				rec_buff[i]=0;
			count = 0; //计数器置零
				
			
		}

	}
}

(记得每写一个函数要去.h文件中声明一下)
mainwhile(1)中:

uart_data_rec();

image.png

【2】串口的空闲中断

十三、eeprom读写

①eeprom是非易失性存储器(掉电后数据不丢失)
PS:有两点需要注意
a、主机给从机发信息,从机接到信息后要给主机回应; b、从机给主机发信息,主机接到信息后也要给从机回应; image.png

image.png

image.png 1740462412886.png 由于电路图中E1、E2、E3引脚都是接地的,所以A0、A1、A2都是0,最后一位R/W表示为1时读取,为0时写入;
①创建工程,完成基本配置;
②去蓝桥杯资料中找到“竞赛平台”,将里面的示例的i2c的hal库文件(i2c_hal.hi2c_hal.c)复制到code中;
③打开i2c.c,编写eeprom写入函数(eeprom_write(uint8_t addr,uint8_t dat))以及eeprom读出函数(eeprom_read(uint8_t addr)
PS:参数表中的addr是片内地址(注意与芯片地址区分),有0~255个地址;

//eeprom写入数据(data不能超过0~255这个范围)
void eeprom_write(uint8_t addr, uint8_t dat)//参数表第一个参数是芯片的片内地址,第二个是写的数据
{	
	I2CStart();//开启I2C
	I2CSendByte(0xa0);//发送一个字节的数据;AT24C02地址是1010 0000,1010的16进制就是a,最后一位是0,代表写
	I2CWaitAck();//等待回应
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();//关闭I2C
        
	HAL_Delay(20);//延时20ms,避免连续写入出错,因为eeprom写入需要时间
}

uint8_t eeprom_read(uint8_t addr)//eeprom从主机读数据
{
	I2CStart();
	I2CSendByte(0xa0);//锁定从机地址
	I2CWaitAck();
	I2CSendByte(addr);//告诉主机要读哪个地址的数据
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);//写指令
	I2CWaitAck();
	uint8_t dat = I2CReceiveByte();
	I2CSendNotAck();//主机不发送回应
	I2CStop();
	
	return dat;
}

main函数中记得#include "i2c_hal.h",
while(1)

  LCD_Init();
  LCD_SetTextColor(White);
  LCD_SetBackColor(Black);
  LCD_Clear(Black);
  I2C_Init();
  
  
  eeprom_write(0,11);
  uint8_t dat = eeprom_read(0);
  char text[20];

while(1)

	sprintf(text,"     %d      ",dat);
	LCD_DisplayStringLine(Line0,(uint8_t *)text);

十四、RTC实时时钟

主要功能实现:

1、设置时钟和日期
2、读取时间和日期
3、设置一个闹钟
①新建工程,完成Cube基本配置,生成代码;
②Cube设置:先在TIM中找到RTC,分别使能时钟源、使能日历,选择Alarm A(闹钟A)
如下图: image.png image.png

image.png 芯片引脚配置: image.png

image.png ③写一个获取时间和日期的函数 sTimesDate的定义可以参考rtc.c中的定义

char text[20];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
void lcd_show()
{	
	HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);//最后的参数是决定要二进制(RTC_FORMAT_BIN)还是十进制(RTC_FORMAT_BCD)
	HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
	sprintf(text,"       text        ");
	LCD_DisplayStringLine(Line0,(uint8_t *)text);
	sprintf(text,"     %2d:%2d:%2d     ",sTime.Hours,sTime.Minutes,sTime.Seconds);
	LCD_DisplayStringLine(Line3,(uint8_t *)text);
	sprintf(text,"    %d-%d-%d-%d     ",sDate.Year,sDate.Month,sDate.Date,sDate.WeekDay);
	LCD_DisplayStringLine(Line5,(uint8_t *)text);

}

PS:即使不需要获取日期,也要将获取日期的函数添上,不然时间不会流动 ④编写闹钟函数 在lcd_show()函数中加入 led_show(1,led_mode);//最好不要放在中断里 再在fun.c文件中写一个中断函数

uint8_t led_mode;
void  HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
	led_mode = 1;
}

(要最终实现闹钟,还需要参考前面解决led与lcd冲突的篇章)

2025-02-26 16:45更新至此

PS

image.png

image.png

image.png