第六届 蓝桥杯 嵌入式 省赛
参考第六届蓝桥杯嵌入式省赛步调筹划题剖析(基于HAL库)_蓝桥杯嵌入式第六届真题-CSDN博客
一、分析功能
RTC 定时
1)时间初始化
2)定时上报电压时间
ADC测量
采集电位器的输出电压信号。
串行功能
1)传送要设置的k值
2)传送上报的电压
3)保存 E2PROM
LED指示灯
电压大于阈值时,闪耀。
LCD显示
1)电位器输出电压
2)k 值
3)LED闪耀开关状态
4)系统时间
按键
1)开关LED闪耀
2)设置上报时间
3)切换时、分、秒
4)调整时间
整体逻辑图
https://i-blog.csdnimg.cn/direct/2ed374a4f8984d8f9e328987f740c7d2.png
二、CubeMX 配置
1.根本配置
新建一个工程,选择芯片 STM32G431RBT6
https://i-blog.csdnimg.cn/direct/53e570fb2eda4ac5bae9566056b39752.png
配置系统 SYS
https://i-blog.csdnimg.cn/direct/335b1890d82d4e25bf5ade2a87875c76.png
修改制止优先级
https://i-blog.csdnimg.cn/direct/65d75547f8fb4b42b6c975797b7e41f6.png
配置时钟RCC
https://i-blog.csdnimg.cn/direct/5a7656304a924d75bcaa8ef6d4b64308.png
https://i-blog.csdnimg.cn/direct/39f2e66a13854ac3aa8fe3a335d481a0.png
时钟树设置:
https://i-blog.csdnimg.cn/direct/74e74a869b5347de9c744f78b93276b5.png
输入频率其实就是晶振提供的24MHz,采用HSE(高速外部时钟源)。至于SYSCLK 系统时钟的80MHz 可以通过 PLLM、PLL配置出来。
2. KEY+LED
https://i-blog.csdnimg.cn/direct/4f12b271105e4b9b884455693331698d.png
https://i-blog.csdnimg.cn/direct/ff0f335f8a7a46dfab63172acb80c02d.png
设置电气属性
将 PC8-PC15 以及 PD2 设置为输出;
将 PB0,PB1,PB2,PA0 设置为 输入。
https://i-blog.csdnimg.cn/direct/5987757aa8a14246a11e2671f178be70.png
设置 LED 的初始状态
由于LED低有用,故设置LED的输出设置成High-----这样上电的时候灯是熄灭的
https://i-blog.csdnimg.cn/direct/820c1591bc0d46da897d4873ec2d13c8.png
3. UART
https://i-blog.csdnimg.cn/direct/4598b5b1e7e141ff9c61f8c158cbd03d.png
https://i-blog.csdnimg.cn/direct/850d7b4d0ab144a69348fe4417ca8463.png
设置MODE为异步通信(Asynchronous) 修改波特率9600(根据题目修改) NVIC Settings一栏使能继承制止 4. ADC
https://i-blog.csdnimg.cn/direct/f62b9e59bace4e2cb7ca4cb05a7307b2.png
https://i-blog.csdnimg.cn/direct/45200c21a02e4ed8b3a8905c601b559f.png
https://i-blog.csdnimg.cn/direct/736599969dd34faf98ca980128eabc99.png
https://i-blog.csdnimg.cn/direct/818e444f24454f96a89d534f11b478d5.png
https://i-blog.csdnimg.cn/direct/acbc8a14977841e88dce097ca090cc3d.png
https://i-blog.csdnimg.cn/direct/6203241416194fdf9d9d733b3f8e9e59.png
5. TIM
https://i-blog.csdnimg.cn/direct/a505e42014ff4ac79f448c113d8347cf.png
https://i-blog.csdnimg.cn/direct/8e622e470c8347ca9e2c1563b07fab96.png
使能定时器6(根本定时器)
https://i-blog.csdnimg.cn/direct/2a183671478a4bcdae6d98d7f7b5b104.png
https://i-blog.csdnimg.cn/direct/e09edfe5fd1546198797627dfb7a4351.png
配置定时器2(输入捕获)
PA15选择定时器2的通道1
https://i-blog.csdnimg.cn/direct/e15f170c63164b35a41d6c9f721c77fa.png
https://i-blog.csdnimg.cn/direct/ee0f9ace66fd4e52af4dc48ebc7a0507.png
https://i-blog.csdnimg.cn/direct/8668b7a1783a492b91c9d73272598051.png https://i-blog.csdnimg.cn/direct/87939415a3ed4f539cbd2266f651c6c8.png
https://i-blog.csdnimg.cn/direct/9724913e80d44ca4bcdb3a359d1cb1c0.png
配置定时器3(输出PWM)
https://i-blog.csdnimg.cn/direct/130511ffe5ce4a00992537186e303da9.png
选择80分频,一兆的频率进行1000计数,频率就是1000HZ,使能自动重装载。
https://i-blog.csdnimg.cn/direct/942172a2a62142899a243d4c3b7d91bb.png
TIM3的占空比设置成30%,则脉冲就设置成300
https://i-blog.csdnimg.cn/direct/9543a92fbba240d8be2b9975f2437872.png
TIM17的占空比设置成60%,则脉冲设置成600
配置定时器15(输出方波---是比较输出模式)
https://i-blog.csdnimg.cn/direct/779981dab94d499a98ef20825df99b0d.png
https://i-blog.csdnimg.cn/direct/0dcac093729f4ef791e864c8e23ba816.png
https://i-blog.csdnimg.cn/direct/ee348cd270b3405bab21364f447fe461.png
https://i-blog.csdnimg.cn/direct/732120aafa0c426ab4478f39b3c0eb55.png
https://i-blog.csdnimg.cn/direct/afdd1f3d02d34799b820ee02a846e5be.png
6. RTC
https://i-blog.csdnimg.cn/direct/b24208ec7144497fa9a0f2aa14bfda66.png
https://i-blog.csdnimg.cn/direct/5b3c0c62ca9f436c8e4def03f83cccbf.png
RTC时钟频率 = RTC时钟源 / ((Asynchronous Predivider value + 1) * (Synchronous Predivider value + 1))
https://i-blog.csdnimg.cn/direct/58dcfb05d96b44f8805ff5375cebb606.png
7. 天生代码
https://i-blog.csdnimg.cn/direct/899fb19a7ecd4309bdbcd1fe420f296b.png
https://i-blog.csdnimg.cn/direct/01e2262eee71421faa2e23279f9e5891.png
https://i-blog.csdnimg.cn/direct/4a7b9fcfd92149a89bd0608ffe086465.png
三、编写代码
mcu 开发的架构层次:
https://i-blog.csdnimg.cn/direct/6591ebd4615545f6a87c921005c1366b.png
由于蓝桥杯是没有 RTOS 的,且BSP和HAL都是现成的。只要开发应用层和修改MCU即可即可。
接下来是对应用层的开发。
1. 头文件
mcu编程和系统编程的代码风格有点不一样。mcu貌似把头文件全放main.c上了,而系统编程都是放在 *.h 中。可能mcu的代码量不够多,没必要这么写。
Cube设定好后,会有这些应用层的文件:
https://i-blog.csdnimg.cn/direct/0896200ed12449098cd15e8f29c116fa.png
也就是 ADC、RTC、UART、TIM。
LED和KEY是不用再另起一个文件写,但是为了书写规范,还是打算写一个key_led.c。
而LCD是需要从赛点资源库导入进来的。
https://i-blog.csdnimg.cn/direct/2e2ff628e8c14480b0a6c0b981b518fa.png
https://i-blog.csdnimg.cn/direct/bd47c66d2d9d43788e32a1d27e1c5ea5.png
https://i-blog.csdnimg.cn/direct/f658034a01da4825a86c0d5677269f26.png
https://i-blog.csdnimg.cn/direct/73a0e7720e3a47d7abdee95829bd55ba.png
https://i-blog.csdnimg.cn/direct/f14d815deb18489d821758de722af390.png
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "rtc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "i2c_hal.h" 2. 函数声明和主函数
函数分析
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void); 主函数
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_ADC1_Init();
MX_ADC2_Init();
MX_TIM2_Init();
MX_TIM6_Init();
MX_USART1_UART_Init();
MX_RTC_Init();
MX_TIM3_Init();
MX_TIM15_Init();
MX_TIM17_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Clear(White);
LCD_SetBackColor(White);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
Key_Proc();
Led_Proc();
Lcd_Proc();
Usart_Proc();
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3. 函数定义
3.1 按键、LCD
我们重新审题:
B1:LED灯的打开和关闭
B2:LCD 的切换
B3:切换时分秒
B4:修改时分秒
时间窗口
//*减速变量
//方式1
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
if((uwTick -uwTick_Key_Set_Point)<50) return;//减速函数
uwTick_Key_Set_Point = uwTick;
//方式2
uint32_t uskey;
//stm32g4xx_it.c
/* USER CODE BEGIN PV */
extern uint32_t uskey;
/* USER CODE END PV */
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
uskey++;//实现usled每隔1ms自增1
/* USER CODE END SysTick_IRQn 1 */
} 两种写法,挑一个。
按键模版
//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;
ucKey_Val = Key_Scan();
unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Old = ucKey_Val;
范例的3行按键模版,背就完了。
uint8_t Key_Scan(void);
uint8_t Key_Scan(void)
{
uint8_t key_val=0;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=1;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
{
key_val=2;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
{
key_val=3;
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=4;
}
return key_val;
} B1按键功能实现
void Key_Proc(void)
{
if((uwTick -uwTick_Key_Set_Point)<50) return;//减速函数
uwTick_Key_Set_Point = uwTick;
ucKey_Val = Key_Scan();
unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Old = ucKey_Val;
//B1完成LED报警功能的打开和关闭
if(unKey_Down == 1)
{
LED_Ctrl ^= 0x01;//让最后一位翻滚
}
} 对LED异或1是切换状态的根本操纵。
LCD显示
LCD的切换,就是清屏后,再刷上去。
LCD 的两个界面:
界面1
1、电位器输出电压
2、k值
3、LED的状态
4、系统时间
界面2
1、Setting
2、XX-XX-XX
延时后,读取电压和RTC,而电压与RTC的实现后面会实现。
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
float R37_Voltage;
RTC_TimeTypeDef H_M_S_Time;
RTC_DateTypeDef Y_M_D_Date;
void Lcd_Proc(void)
{
if((uwTick -uwTick_Lcd_Set_Point)<100) return;//减速函数
uwTick_Lcd_Set_Point = uwTick;
//数据采集区
R37_Voltage = ((((float)getADC2())/4096)*3.3);
HAL_RTC_GetTime(&hrtc, &H_M_S_Time, RTC_FORMAT_BIN);//读取日期和时间必须同时使用
HAL_RTC_GetDate(&hrtc, &Y_M_D_Date, RTC_FORMAT_BIN);
}
那么我们要设置两个page
page0:数据显示区
uint8_t Interface_Num;//0x00-显示界面,0x10-设置上报时间的小时,0x11-设置分钟,0x12-设置秒。
uint8_t Lcd_Disp_String;//最多显示20个字符
uint8_t k_int = 1;
void Lcd_Proc(void)
{
......
if(Interface_Num == 0x00) //page0
{
sprintf((char *)Lcd_Disp_String, " V1:%4.2fV", R37_Voltage);
LCD_DisplayStringLine(Line2, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, " k:%3.1f", (k_int*0.1));
LCD_DisplayStringLine(Line4, Lcd_Disp_String);
if(LED_Ctrl == 0)
sprintf((char *)Lcd_Disp_String, " LED:ON");
else
sprintf((char *)Lcd_Disp_String, " LED:OFF");
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, " T:%02d-%02d-%02d", (unsigned int)H_M_S_Time.Minutes, (unsigned int)H_M_S_Time.Second;
LCD_DisplayStringLine(Line8, Lcd_Disp_String);
}
} UART传输的数据,后面会实现。
page1:时间设置区
设置的 Interface_Num 为 0x10,以及右移4位,能够分身 0x10,0x11,0x12 三个状态保持在同Page 中。
__IO uint32_t uwTick_SETTING_TIME_Set_Point = 0;//控制待设置的时间数值闪烁
uint8_t SETTING_TIME_Ctrl = 0;// 0-亮,1-灭,控制时间设置界面的待设置值的闪烁功能
void Lcd_Proc(void)
{
......
//时间设置区
if((Interface_Num>>4) == 0x1) //进入设置界面
{
sprintf((char *)Lcd_Disp_String, " Setting");
LCD_DisplayStringLine(Line2, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, " T:%02d-%02d-%02d", (unsigned int)Clock_Comp_Disp, (unsigned int)Clock_Comp_Disp, (unsigned int)Clock_Comp_Disp);
if((uwTick - uwTick_SETTING_TIME_Set_Point)>=500)
{
uwTick_SETTING_TIME_Set_Point = uwTick;
SETTING_TIME_Ctrl ^= 0x1;
}
if(SETTING_TIME_Ctrl == 0x1) //控制闪烁,时间设置的时候闪烁
{
if(Interface_Num == 0x10) //设置时
{
Lcd_Disp_String = ' ';
Lcd_Disp_String = ' ';
}
else if(Interface_Num == 0x11) //设置分
{
Lcd_Disp_String = ' ';
Lcd_Disp_String = ' ';
}
else if(Interface_Num == 0x12) //设置秒
{
Lcd_Disp_String = ' ';
Lcd_Disp_String = ' ';
}
}
LCD_DisplayStringLine(Line5, Lcd_Disp_String);
}
} RTC的时间后面会实现。
B2按键功能实现
uint8_t Clock_Comp_Disp = {0,0,0};//闹钟比较值的初值(显示专用)
uint8_t Clock_Comp_Ctrl = {0,0,0};//闹钟比较值的初值(控制专用)
void Key_Proc(void)
{
......
//B2完成两个界面的切换
if(unKey_Down == 2)
{
if(Interface_Num == 0x00) //数据显示区
{
LCD_Clear(White); //清屏
Interface_Num = 0x10;
}
else if((Interface_Num>>4) == 0x1)
{
LCD_Clear(White); //清屏
Interface_Num = 0x00;
Clock_Comp_Ctrl = Clock_Comp_Disp; //更新闹钟显示值到控制值
Clock_Comp_Ctrl = Clock_Comp_Disp; //更新闹钟显示值到控制值
Clock_Comp_Ctrl = Clock_Comp_Disp; //更新闹钟显示值到控制值
}
}
}
B3按键功能实现
void Key_Proc(void)
{
...
//B3切换时分秒,切换时会闪烁
if(unKey_Down == 3)
{
if((Interface_Num>>4) == 0x1)
//时分秒循环切换
if(++Interface_Num == 0x13)
Interface_Num = 0x10;
}
}
B4功能实现
时间设定,具体是在 LCD 部门实现。按键只实现时间重置而已。
设置的 Interface_Num 为 0x10,以及右移4位,能够分身 0x10,0x11,0x12 三个状态保持在同Page 中。
void Key_Proc(void)
{
......
//B4调整时间,其实按键这块只是实现时间到底后回到0
if(unKey_Down == 4)
{
if(Interface_Num == 0x10)
{
if( ++Clock_Comp_Disp ==24)
Clock_Comp_Disp = 0;
}
if(Interface_Num == 0x11)
{
if( ++Clock_Comp_Disp ==60)
Clock_Comp_Disp = 0;
}
if(Interface_Num == 0x12)
{
if( ++Clock_Comp_Disp ==60)
Clock_Comp_Disp = 0;
}
}
}
3.2 LED
重新审题
当 V 1 >V DD *k 时,指示灯LD1 以 0.2 秒为隔断闪耀,闪耀功能可以通过按键关闭。 __IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
uint8_t ucLed;
__IO uint32_t uwTick_LED_bulingbuling_Set_Point = 0;//控制LED报警闪烁的打点变量
void Led_Proc(void){
if((uwTick -uwTick_Led_Set_Point)<50) return;//减速函数
uwTick_Led_Set_Point = uwTick;
if(LED_Ctrl == 0x1)//关闭LED的功能的时候
ucLed = 0x00;
else//开启LED功能的时候
{
if(R37_Voltage>=(3.3*k_int*0.1))
{
//200ms 闪烁
if((uwTick-uwTick_LED_bulingbuling_Set_Point)>=200)
{
uwTick_LED_bulingbuling_Set_Point = uwTick;
ucLed ^= 0x1;
}
}
else
ucLed = 0x00;
}
LED_Disp(ucLed);
}void LED_Disp(uint8_t ucLed);
void LED_Disp(uint8_t ucLed)
{
//**将所有的灯熄灭
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
//根据ucLed的数值点亮相应的灯
HAL_GPIO_WritePin(GPIOC, ucLed<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
3.3 串口
重新审题
定时上报电压V1
格式:【V1电压值】+【k 值】+【时间】【命令结束标志】
“2.21+0.1+123030\n” 12 时 30 分 30 秒上报电压值为 2.21V , k 值为 0.1 串口接收数据 当第一个字符收到 k,则开始缓存。 uint8_t rx_buffer;
uint8_t rx_buf;//接收到的指令临时存放缓冲区
uint8_t rx_buf_index = 0;//控制数据往buf里边存储的顺序
_Bool Start_Flag;//起始位判断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if((rx_buffer == 0x6B)&&(rx_buf_index == 0))
{
Uart_Rev_Data_Delay_Time = uwTick;//接收到第一个数据启动计时
Start_Flag = 1;
}
if(Start_Flag == 1)
{
rx_buf = rx_buffer;
rx_buf_index++;
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)(&rx_buffer), 1);
} 串口数据处置惩罚 uint8_t Ctrl_Uart_Send_Time_Data_Times = 0;// 控制只允许到闹钟时间后只上报一次
__IO uint32_t Uart_Rev_Data_Delay_Time = 0;//控制串口接收数据的等待时间
_Bool Start_Flag;//起始位判断
uint16_t counter = 0;
uint8_t str;
uint8_t rx_buffer;
uint8_t rx_buf;//接收到的指令临时存放缓冲区
uint8_t rx_buf_index = 0;//控制数据往buf里边存储的顺序
void Usart_Proc(void){
if((uwTick -uwTick_Usart_Set_Point)<30) return;//减速函数
uwTick_Usart_Set_Point = uwTick;
//闹钟时间到
if((H_M_S_Time.Hours == Clock_Comp_Ctrl&&(H_M_S_Time.Minutes == Clock_Comp_Ctrl&&(H_M_S_Time.Seconds == Clock_Comp_Ctrl))))
{
//控制只发送一次数据
if(Ctrl_Uart_Send_Time_Data_Times == 0)
{
Ctrl_Uart_Send_Time_Data_Times = 1;
sprintf(str, "%4.2f+%3.1f+%02d%02d%02d\n", R37_Voltage, (k_int*0.1), (unsigned int)H_M_S_Time.Hour, (unsigned int)H_M_S_Time.Minute, (unsigned int)H_M_S_Time.Second);
HAL_UART_Transmit(&huart1, (unsigned char *) str, strlen(strlen), 50);
}
}
else
//当时间变化或者控制值变化,两者不等的时候,恢复下一次数据发送允许。
Ctrl_Uart_Send_Time_Data_Times = 0;
//串口接收的数据处理
if(((uwTick - Uart_Rev_Data_Delay_Time)<=300)&&(uwTick - Uart_Rev_Data_Delay_Time)>=200)//200ms~300ms之内处理数据
{
if(rx_buf_index == 6) //接收到6个数据
{
if((rx_buf == 0x6B)&&(rx_buf == 0x30)&&(rx_buf == 0x2E)&&(rx_buf == 0x5C)&&(rx_buf == 0x6E))
{
if((rx_buf>=0x31)&&(rx_buf<=0x39))
{
k_int = rx_buf - 0x30;
sprintf(str, "OK\n");
HAL_UART_Transmit(&huart1, (unsigned char *) strlen, strlen(strlen), 50);
iic_24c02_write(&k_int, 0, 1);
}
}
}
rx_buf_index = 0;
Start_Flag = 0;
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if((rx_buffer == 0x6B)&&(rx_buf_index == 0))
{
Uart_Rev_Data_Delay_Time = uwTick;//接收到第一个数据启动计时
Start_Flag = 1;
}
if(Start_Flag == 1)
{
rx_buf = rx_buffer;
rx_buf_index++;
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)(&rx_buffer), 1);
}发送数据没啥好说的,而接收 "k0.x\n" 需要注意,总共就6个元素:
[*] 检查数据是否符合特定格式:
[*] 0x6B 对应ASCII字符 'k'
[*] 0x30 对应ASCII字符 '0'
[*] 0x2E 对应ASCII字符 '.'
[*] 0x5C 对应ASCII字符 ''
[*] 0x6E 对应ASCII字符 'n'
除了 buf,其余都要检查。
最后 使用 iic_24c02_write 将k的数值写到k_int。
3.4 RTC
在前面,我已经定义过这两个变量了,这里轻微再提一下。
RTC_TimeTypeDef H_M_S_Time;
RTC_DateTypeDef Y_M_D_Date;
HAL_RTC_GetTime(&hrtc,&time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&date,RTC_FORMAT_BIN);
3.5 ADC
uint16_t getADC1(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc1);
adc = HAL_ADC_GetValue(&hadc1);
return adc;
}
uint16_t getADC2(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc2);
adc = HAL_ADC_GetValue(&hadc2);
return adc;
}3.6 RCC
这部门,没有什么需要用户本身编写的地方
3.7 I2C
void iic_24c02_write(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
while(ucNum--)
{
I2CSendByte(*pucBuf++);
I2CWaitAck();
}
I2CStop();
HAL_Delay(500);
}void iic_24c02_read(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
while(ucNum--)
{
*pucBuf++=I2CReceiveByte();
if(ucNum)
I2CSendAck();
else
I2CSendNotAck();
}
I2CStop();
}
4. 代码规范
4.1 MAIN
main.c 头文件
系统文件和Cube自动配置的:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "rtc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h> // 提供 sprintf()
#include <string.h>// 提供 strlen()第三方导入文件:
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "i2c_hal.h"
/* USER CODE END Includes */结构体变量
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
RTC_TimeTypeDef H_M_S_Time;
RTC_DateTypeDef Y_M_D_Date;
/* USER CODE END PTD */用户定义的全局变量
//*减速变量
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
__IO uint32_t uwTick_Usart_Set_Point = 0;//控制Usart_Proc的执行速度
//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;
//*LED专用变量
uint8_t ucLed;
//*LCD显示专用变量
uint8_t Lcd_Disp_String;//最多显示20个字符
//*串口专用变量
uint16_t counter = 0;
char str;
uint8_t rx_buffer;
uint8_t rx_buf;//接收到的指令临时存放缓冲区
uint8_t rx_buf_index = 0;//控制数据往buf里边存储的顺序
//用户自定义变量区
uint8_t Interface_Num;//0x00-显示界面,0x10-设置上报时间的小时,0x11-设置分钟,0x12-设置秒。
float R37_Voltage;
uint8_t k_int = 1;
uint8_t LED_Ctrl = 0;// 0-打开,1关闭,控制LED报警功能
uint8_t Clock_Comp_Disp = {0,0,0};//闹钟比较值的初值(显示专用)
uint8_t Clock_Comp_Ctrl = {0,0,0};//闹钟比较值的初值(控制专用)
__IO uint32_t uwTick_SETTING_TIME_Set_Point = 0;//控制待设置的时间数值闪烁
uint8_t SETTING_TIME_Ctrl = 0;// 0-亮,1-灭,控制时间设置界面的待设置值的闪烁功能
uint8_t Ctrl_Uart_Send_Time_Data_Times = 0;// 控制只允许到闹钟时间后只上报一次
__IO uint32_t Uart_Rev_Data_Delay_Time = 0;//控制串口接收数据的等待时间
_Bool Start_Flag;//起始位判断
__IO uint32_t uwTick_LED_bulingbuling_Set_Point = 0;//控制LED报警闪烁的打点变量函数声明
/* USER CODE BEGIN PFP */void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);/*这部门封装到BSP中*///uint8_t Key_Scan(void);//void LED_Disp(uint8_t ucLed);//void iic_24c02_write(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum);/* USER CODE END PFP */不过,我更习惯将函数声明放在 main.h 中。
4.2 BSP
gpio
LED的 void LED_Disp(uint8_t ucLed) 和 KEY的 uint8_t Key_Scan(void) 可以封装到 gpio.c 中。
//gpio.h
/* USER CODE BEGIN Prototypes */
uint8_t Key_Scan(void);
void LED_Disp(uint8_t ucLed);
/* USER CODE END Prototypes */
//gpio.c
/* USER CODE BEGIN 2 */
//LED扫描
void LED_Disp(uint8_t ucLed)
{
//**将所有的灯熄灭
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
//根据ucLed的数值点亮相应的灯
HAL_GPIO_WritePin(GPIOC, ucLed<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
//按键扫描
uint8_t Key_Scan(void)
{
uint8_t key_val=0;
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=1;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
{
key_val=2;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
{
key_val=3;
}
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
{
key_val=4;
}
return key_val;
}
/* USER CODE END 2 */ADC
//adc.h/* USER CODE BEGIN Prototypes */uint16_t getADC2(void);uint16_t getADC1(void);/* USER CODE END Prototypes *///adc.c/* USER CODE BEGIN 1 */uint16_t getADC1(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc1);
adc = HAL_ADC_GetValue(&hadc1);
return adc;
}
uint16_t getADC2(void)
{
uint16_t adc = 0;
HAL_ADC_Start(&hadc2);
adc = HAL_ADC_GetValue(&hadc2);
return adc;
}/* USER CODE END 1 */
I2C
//i2c_hal.hvoid iic_24c02_read(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum);void iic_24c02_write(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum);//i2c_hal.cvoid iic_24c02_write(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
while(ucNum--)
{
I2CSendByte(*pucBuf++);
I2CWaitAck();
}
I2CStop();
HAL_Delay(500);
}void iic_24c02_read(uint8_t* pucBuf, uint8_t ucAddr, uint8_t ucNum)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(ucAddr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
while(ucNum--)
{
*pucBuf++=I2CReceiveByte();
if(ucNum)
I2CSendAck();
else
I2CSendNotAck();
}
I2CStop();
}
其他
像 LCD、UART、TIM、RTC 就不用封装了,在 Main 中实现即可。
四、测试
烧录完,测试时,会发现设置时间时,闪耀的位置不对。
应该把 Lcd_Proc 的时间设置区的控制闪耀代码进行更改。
固然如果要居中显示,就要看看本身整了多少空格。
// 根据当前设置项闪烁对应位置
if (SETTING_TIME_Ctrl == 0x1)// 闪烁状态
{
switch (Interface_Num)
{
case 0x10:// 设置时(闪烁 HH 部分)
Lcd_Disp_String = ' ';// 第1个数字
Lcd_Disp_String = ' ';// 第2个数字
break;
case 0x11:// 设置分(闪烁 MM 部分)
Lcd_Disp_String = ' ';// 第1个数字
Lcd_Disp_String = ' ';// 第2个数字
break;
case 0x12:// 设置秒(闪烁 SS 部分)
Lcd_Disp_String = ' ';// 第1个数字
Lcd_Disp_String = ' '; // 第2个数字
break;
default:
break;
}
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]