拉不拉稀肚拉稀 发表于 2025-4-5 23:29:44

第六届 蓝桥杯 嵌入式 省赛

参考

第六届蓝桥杯嵌入式省赛步调筹划题剖析(基于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]
查看完整版本: 第六届 蓝桥杯 嵌入式 省赛