一、题目分析
需要用到的环境以及题目所要求设计的模块
开发板:CT117E-M4 (STM32G431RBT6)
开发环境:Keil5(MDK)+CubeMX
设计任务及要求如下:
1、RTC实时时钟
2、ADC测量R37的输出电压
3、USART串口
4、LED闪烁、LCD显示
5、按键功能
二、CubeMX配置
老规矩,我们还是在官方给的液晶驱动程序中的MDK5_LCD_HAL文件中进行修改,再将官方给的i2c_hal.c和i2c_hal.h两个文件分别加入到Src和Inc中,这样可以保留一些最原始的配置,也可以节省一点时间。
1、配置LED和按键
2、配置RTC实时时钟
3、配置ADC
4、配置USART串口
5、配置Project Manager
三、相关代码实现
1、函数相关定义
#include "lcd.h"
#include "stdio.h"
#include "string.h"
#include "i2c_hal.h"
_Bool ui = 0;
_Bool led1_flag = 1;
float r37_volt = 0;
float k = 0.1;
RTC_TimeTypeDef T;
RTC_DateTypeDef D;
u8 time[3] = {0};
u8 time_valid[3] = {0};
u8 tx_buff[20];
u8 rx_buff[20];
u8 rx_date[2];
u8 rx_pointer = 0;
u32 lcd_tick = 0;
u32 key_tick = 0;
u32 led_tick = 0;
u32 rx_tick = 0;
u32 tx_tick = 0;
u8 key3_flag = 0;
u8 led_ctrl = 0x00;
u32 adc_value = 0;
2、LCD模块
void LCD_Proc()
{
if(uwTick - lcd_tick < 200)
lcd_tick = uwTick;
u8 LCD_Buff[20];
if(ui == 0)
{
sprintf((char *)LCD_Buff," V1:%4.2fV",r37_volt);
LCD_DisplayStringLine(Line2,LCD_Buff);
sprintf((char *)LCD_Buff," k:%2.1f",k);
LCD_DisplayStringLine(Line4,LCD_Buff);
if(led1_flag == 0)
sprintf((char *)LCD_Buff," LED:%s ","OFF");
else
sprintf((char *)LCD_Buff," LED:%s ","ON");
LCD_DisplayStringLine(Line6,LCD_Buff);
sprintf((char *)LCD_Buff," T:%02d-%02d-%02d",T.Hours,T.Minutes,T.Seconds);
LCD_DisplayStringLine(Line8,LCD_Buff);
LCD_DisplayStringLine(Line9, (unsigned char *)" 1");
}
else
{
sprintf((char *)LCD_Buff," Setting");
LCD_DisplayStringLine(Line3,LCD_Buff);
sprintf((char *)LCD_Buff," %02d-%02d-%02d",time[0],time[1],time[2]);
LCD_DisplayStringLine(Line5,LCD_Buff);
LCD_DisplayStringLine(Line9, (unsigned char *)" 2");
}
}
3、LED模块
void LED_Display(u8 dsled)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,dsled<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_Proc()
{
if(uwTick - led_tick < 200) return;
led_tick = uwTick;
if((r37_volt > (3.3 * k)) && led1_flag)
{
if(led_ctrl & 0x01)
led_ctrl &= ~0x01;
else
led_ctrl |= 0x01;
}
else
led_ctrl &= ~0x01;
LED_Display(led_ctrl);
}
4、RTC模块
//RTC实时时钟
void RTC_Proc()
{
HAL_RTC_GetTime(&hrtc,&T,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&D,RTC_FORMAT_BIN);
}
5、ADC模块
//R37的电压输入
void ADC_Proc()
{
HAL_ADC_Start(&hadc2);
adc_value = HAL_ADC_GetValue(&hadc2);
r37_volt = adc_value * 3.3 / 4095.0f;
}
6、USART模块
//printf重定向输出到串口
struct __FILE
{
int handle;
};
FILE __stdout;
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1,(u8 *)&ch,1,50);
return ch;
}
//串口发送
void TX_Proc()
{
if(uwTick - tx_tick < 500) return;
tx_tick = uwTick;
if(T.Hours == time_valid[0] && T.Minutes == time_valid[1] && T.Seconds == time_valid[2])
{
printf("%4.2f+%3.1f+%02d%02d%02d\n",r37_volt,k,time_valid[0],time_valid[1],time_valid[2]);
}
}
//串口接收
void RX_Proc()
{
if(uwTick - rx_tick < 50) return;
rx_tick = uwTick;
if(rx_pointer == 5 && rx_buff[0] == 'k' && rx_buff[1] == '0' && rx_buff[2] == '.' && rx_buff[4] == '\n')
{
if(rx_buff[3]>='0' && rx_buff[3]<='9')
{
k = (rx_buff[3] - '0') / 10.0f;
printf("ok\n");
E2PROM_Write(0x01,rx_buff[3] - '0');
}
else
{
printf("请输入正确的数字!");
}
}
else if(rx_pointer > 0)
{
printf("输入格式有误,请重新输入!\r\n");
}
rx_pointer = 0;
memset(rx_buff,'\0',sizeof(rx_buff));
}
//中断回调函数---接收数据
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
rx_tick = uwTick;
HAL_UART_Receive_IT(&huart1,rx_date,1);
rx_buff[rx_pointer++] = rx_date[0];
}
7、KEY模块
#define k1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define k2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define k3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define k4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define key k1|(k2<<1)|(k3<<2)|(k4<<3)|0xf0
u8 Tag;
u8 Cnt;
void KEY_Read()
{
u8 keydate = (key) ^ 0xff;
Tag = keydate & (keydate ^ Cnt);
Cnt = keydate;
}
void KEY_Proc()
{
if(uwTick - key_tick < 10) return;
key_tick = uwTick;//过滤掉按键产生的抖动,10ms执行一次
KEY_Read();
if(Tag & 0x01)
{
led1_flag =! led1_flag;
}
if(Tag & 0x02)
{
ui =! ui;
LCD_Clear(White);
time_valid[0] = time[0];
time_valid[1] = time[1];
time_valid[2] = time[2];
}
if(Tag & 0x04)
{
key3_flag = (key3_flag + 1) % 3;
}
if(Tag & 0x08)
{
time[key3_flag] ++;
if(time[0] == 24)
time[0] = 0;
if(time[1] == 60)
time[1] = 0;
if(time[2] == 60)
time[2] = 0;
}
}
四、总结
这届赛题作为蓝桥杯最开始举行的赛题,知识面涉及的还是比较全面的,其中还穿插了E2PROM和USART,这两个考点我认为是最具有挑战性的,以及调节时间的闪烁(这里我没有写出来,如过想要更加细节的实现效果,请转b站老王),我这里也是听了他的讲解才知道Systick系统定时器还能这么用,都不用开TIM定时器了,节省了很多代码,最终也是顺利实现的本届程序设计题的所有功能。