蓝桥杯嵌入式省赛快速入门教程
本文最后更新于226 天前,其中的信息可能已经过时,如有错误请发送邮件到553212114@qq.com

00.新建工程(CubeMX)

1.Debug配置

点击魔术棒,debug改为cmsis,setting里设置reset and run。

2.文件目录配置

新建code文件,存放自己的代码;然后在keil内新建code文件夹

在魔术棒内添加code文件路径路径

在code内新建文件,注意:新建文件时不要忘记选择保存路径在code内!!!

新建headfile.h文件,添加标准头文件

#include "stm32g4xx.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>

01.LED

原理图

锁存器当PD2为高电平时数据才会开启传送,PC8-PC15置低电平的时候LED亮

第一步先把PD2置为高电平

第二步把LED置为低电平

第三步把PD2置为低电平 一定要置为低电平

IO配置

PC8~PC15以及PD2都配置为高电平,让LED上电的时候为高电平,默认熄灭;

注意!!!

PD2开始必须置为高电平才能写入!!!,因为接入锁存器锁定状态;

写入结束PD2必须置为低电平!!!

void led_on(void)
{
        // PD2开始必须置为高电平才能写入,因为接入锁存器锁定状态
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
        
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
        
        // 写入结束PD2必须置为低电平!!!
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

02.按键

按键需要配置为上拉输入,没有按下时输入高电平,按下时输入低电平;

uint8_t key1, key2, key3, key4;
uint8_t key1_state = 0, key2_state = 0, key3_state = 0, key4_state = 0; 
void key_scan(void)
{
        key1 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
        key2 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
        key3 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
        key4 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
        
        if (key1 == 0)
        {
                HAL_Delay(10);
                while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == 0);
                key1_state = !key1_state;
                led_on(1, key1_state);
                led_on(2, key1_state);
        }
        
        if (key2 == 0)
        {
                HAL_Delay(10);
                while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0);
                key2_state = !key2_state;
                led_on(3, key2_state);
                led_on(4, key2_state);
        }
        
        if (key3 == 0)
        {
                HAL_Delay(10);
                while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == 0);
                key3_state = !key3_state;
                led_on(5, key3_state);
                led_on(6, key3_state);
        }
        
        if (key4 == 0)
        {
                HAL_Delay(10);
                while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 0);
                key4_state = !key4_state;
                led_on(7, key4_state);
                led_on(8, key4_state);
        }
}

按键长短按函数

// 按键长按短按
uint8_t key1_state, key2_state, key3_state, key4_state;
uint8_t key1_last_state = 1, key2_last_state = 1, key3_last_state = 1, key4_last_state = 1;
int16_t count = 0;
uint8_t led_mode;
uint8_t lcd_highshow = 0;

void key_scan(void)
{
        key1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
        key2_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
        key3_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
        key4_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
        
        // 按下瞬间
        if (key1_state == 0 && key1_last_state == 1)        // 按键按下
        {
                TIM3->CNT = 0;
        }
        // 一直按
        else if (key1_state == 0 && key1_last_state == 0)
        {
                if (TIM3->CNT >= 10000)
                {
                        count+=1;
                        HAL_Delay(200);
                }
        }
        else if (key1_state == 1 && key1_last_state == 0) // 松开
        {
                if (TIM3->CNT < 10000)
                {
                        count-=1;
                }
        }

        
        if(key2_state == 0 && key2_last_state == 1)  // 按键B2按下
        {
                TIM3->CNT = 0;
        }
        else if(key2_state == 0 && key2_last_state == 0)  // 按键B2一直按着
        {
                if(TIM3->CNT >= 10000)   //按键B2长按1s
                {
                        count+=1;
                        HAL_Delay(200);
                }
        }
        else if(key2_state == 1 && key2_last_state == 0)  // 按键B2松开
        {
                if(TIM3->CNT < 10000)    //按键B2短按
                {
                        count-=1;
                }
        }
        
        // 按下瞬间
        if (key3_state == 0 && key3_last_state == 1)        // 按键按下
        {
                TIM3->CNT = 0;
        }
        // 一直按
        else if (key3_state == 0 && key3_last_state == 0)
        {
                if (TIM3->CNT >= 10000)
                {
                        count+=1;
                        HAL_Delay(200);
                }
        }
        else if (key3_state == 1 && key3_last_state == 0) // 松开
        {
                if (TIM3->CNT < 10000)
                {
                        count-=1;
                }
        }
        
        // 按下瞬间
        if (key4_state == 0 && key4_last_state == 1)        // 按键按下
        {
                TIM3->CNT = 0;
        }
        // 一直按
        else if (key4_state == 0 && key4_last_state == 0)
        {
                if (TIM3->CNT >= 10000)
                {
                        lcd_highshow+=1;
                        HAL_Delay(200);
                }
        }
        else if (key4_state == 1 && key4_last_state == 0) // 松开
        {
                if (TIM3->CNT < 10000)
                {
                        count-=1;
                }
        }
        
        key1_last_state = key1_state;
        key2_last_state = key2_state;
        key3_last_state = key3_state;
        key4_last_state = key4_state;
}

03.LCD

解决LCD与LED引脚冲突问题

首先在LCD_Init();前加上HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

在使用的LCD显示函数前加上uint16_t temp = GPIOx->ODR,如在LCD_Init();里和LCD_Clear(Black);,最后读取原状态,如:

void LCD_Init(void)
{
    uint16_t temp = GPIOC->ODR;
    LCD_CtrlLinesConfig();
    dummy = LCD_ReadReg(0);

    if(dummy == 0x8230)
    {
        REG_8230_Init();
    }
    else
    {
        REG_932X_Init();
    }
    dummy = LCD_ReadReg(0);
    GPIOC->ODR = temp;
}
void LCD_Clear(u16 Color)
{
    uint16_t temp = GPIOC->ODR;
    u32 index = 0;
    LCD_SetCursor(0x00, 0x0000);
    LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
    for(index = 0; index < 76800; index++)
    {
        LCD_WriteRAM(Color);
    }
        GPIOC->ODR = temp;
}
void LCD_DisplayStringLine(u8 Line, u8 *ptr)
{
        uint16_t temp = GPIOC->ODR;
    u32 i = 0;
    u16 refcolumn = 319;//319;

    while ((*ptr != 0) && (i < 20))         //        20
    {
        LCD_DisplayChar(Line, refcolumn, *ptr);
        refcolumn -= 16;
        ptr++;
        i++;
    }
        
        GPIOC->ODR = temp;
}

LCD高亮显示

需要哪行高亮就先使用LCD_SetBackColor();设置为高亮颜色,最后设置为初始化颜色;

char text[20];
void lcd_show()
{
        sprintf(text, "         TEST      ");
        LCD_DisplayStringLine(Line0, (uint8_t *)text);
        lcd_highshow %= 3;
        switch (lcd_highshow)
        {
                case 0:
                                LCD_SetBackColor(Yellow);
                                sprintf(text, "         CNT: %d      ", count);
                                LCD_DisplayStringLine(Line1, (uint8_t *)text);
                                LCD_SetBackColor(Black);
                
                                sprintf(text, "         hello1      ");
                                LCD_DisplayStringLine(Line2, (uint8_t *)text);
                
                                sprintf(text, "         hello2      ");
                                LCD_DisplayStringLine(Line3, (uint8_t *)text);
                        break;
                
                case 1:
                                sprintf(text, "         CNT: %d      ", count);
                                LCD_DisplayStringLine(Line1, (uint8_t *)text);
                                
                                LCD_SetBackColor(Yellow);
                                sprintf(text, "         hello1      ");
                                LCD_DisplayStringLine(Line2, (uint8_t *)text);
                                LCD_SetBackColor(Black);
                
                                sprintf(text, "         hello2      ");
                                LCD_DisplayStringLine(Line3, (uint8_t *)text);
                        break;
                                
                case 2:
                                sprintf(text, "         CNT: %d      ", count);
                                LCD_DisplayStringLine(Line1, (uint8_t *)text);
                
                                sprintf(text, "         hello1      ");
                                LCD_DisplayStringLine(Line2, (uint8_t *)text);
                
                                LCD_SetBackColor(Yellow);
                                sprintf(text, "         hello2      ");
                                LCD_DisplayStringLine(Line3, (uint8_t *)text);
                                LCD_SetBackColor(Black);
                        break;
        }
}

04.PWM

PWM输出

计算公式

输出1000HZ,则PSC=800-1,ARR=100-1;80000000/(800*100)=1000;

首先使用HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);使能TIM的pwm输出功能

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
TIM2->CCR2 = 50;
// CCRx:x为通道号

输入捕获

在波形上升沿/下降沿产生一个中断

第一个上升沿TIMx->CNT = 0;下一个上升沿时uint16_t capture value = TIMx->CNT;即可算出周期T

PSC设置为80-1,能获得更大的计数;

初始化使能HAL_TIM_IC_Start_IT(&htimx, TIM_CHANNEL_x);

使用自定义测量引脚测量输出的PWM频率(PA7测量PA1)

配置PA7为输入捕获,PSC=80-1,使能中断;

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);  //在捕获到上升沿时,会将CNT赋值给CCR ,可写成capture_value = TIM17->CCR1;
        TIM17->CNT = 0;
        fre = 80000000/(80*capture_value);
    }
}

使用原理图指定引脚测量555定时器输出PWM频率

XL555(555定时器)可输出一个频率可调的PWM波频率可通过R39、R40等电位器调整;可以配置PA15或者PB4进行输入捕获测量

    LCD_Init();
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    HAL_TIM_IC_Start_IT(&htim16, TIM_CHANNEL_1);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{ 
    if(htim->Instance == TIM16) //频率输出1 R39
    {
        //在捕获到上升沿时,会将CNT赋值给CCR 
        capture_value1 = TIM16->CCR1;
        //capture_value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);  
        
        TIM16->CNT = 0;
        fre1 = 80000000/(80*capture_value1);
    }
    
    if(htim->Instance == TIM2) //频率输出2 R40
    {
        //在捕获到上升沿时,会将CNT赋值给CCR 
        capture_value2 = TIM2->CCR1;
        //capture_value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        
        TIM2->CNT = 0;
        fre2 = 80000000/(80*capture_value2);
    }
}

05.ADC测量

PB15、PB12引脚配置为ADC输入模式,测量旋转R37、R38电位器变化的电压值

double get_vol(ADC_HandleTypeDef *hadc)
{
    HAL_ADC_Start(hadc);// 一定不能忘记ADC打开函数!!!!!!!!!!
    uint32_t adc_value = HAL_ADC_GetValue(hadc);
    return 3.3*adc_value/4096;
}

06.串口发送与接收

使能中断

串口发送

char text01[20];
sprintf(text01, "Lan Qiao Bei\r\n");
HAL_UART_Transmit(&huart1, (uint8_t *)text01, sizeof(text01), 50);// 50为Timeout时间
HAL_Delay(1000);

串口接收

g4的Rx先接收电脑发过来的数据,存放到rec_data中,然后HAL_UART_Transmit传给电脑,再使用HAL_UART_Receive_IT开启下一次接收

HAL_UART_Receive_IT(&huart1, &rec_data, 1);
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);
    }
}

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

串口接收:每次进入中断只能接收1个字节的数据;

考虑错误情况:

1.每次接收一个字节就要判断该字节是否符合要求(非常复杂)

2.接收完所有字符,判断字符串是否符合要求

解决方法:利用定时器,处理串口接收

串口波特率 = 9600 bit/s 就是1s可以传输9600bit

串口传输一次数据包含起始位,数据位,结束位,一共10bit

10 * 1/9600 = 0.00104 秒 = 1.04 毫秒

核心思想:PSC=8000-1,8000/80000000=1/10000,即100us计数器加1,计数器数到15,1500us,1.5ms,当定时器大于1.5ms没接收到数据时判断数据接收完成。

HAL_UART_Receive_IT(&huart1, &rec_data, 1);
HAL_TIM_Base_Start(&htim4);
char send_buff[20];
uint8_t rec_data, 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);
    }
}

void uart_data_rec()
{
    if(rec_flag)
    {
        if(TIM4->CNT > 15)  //数据接收完成
        {
            //处理数据
            if(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(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(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;
        }
    }
}

07.eeprom读写

EEPROM:非易失性存储器(掉电后数据不丢失)

记住两点:

1.主机给从机发信息,从机接到信息后要给主机回应

2.从机给主机发信息,主机接到信息后也要给从机回应

0XA0(写)/0XA1(读)

软件I2C:要将PB6、PB7设为OUTPUT;

GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStructure.Pull = GPIO_PULLUP;    // 上拉
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
void eeprom_write(uint8_t addr, uint8_t dat)
{
        I2CStart();
        I2CSendByte(0xa0);    // 芯片地址
        I2CWaitAck();
        I2CSendByte(addr);    // 片内地址
        I2CWaitAck();
        I2CSendByte(dat);
        I2CWaitAck();
        I2CStop();
        
        HAL_Delay(20);
}
uint8_t eeprom_read(uint8_t addr)
{
        
        I2CStart();
        I2CSendByte(0xa0);
        I2CWaitAck();
        I2CSendByte(addr);
        I2CWaitAck();
        I2CStop();        
        
        I2CStart();
        I2CSendByte(0xa1);
        I2CWaitAck();
        uint8_t dat = I2CReceiveByte();
        I2CSendNotAck();   //主机不应答 从机不再发数据
        I2CStop();        
        
        return dat;
}

08.rtc实时时钟

基础配置

显示时间

char text[20];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
uint8_t led_mode;
void lcd_show()
{
        HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
        sprintf(text, "      test          ");
        LCD_DisplayStringLine(Line0, (uint8_t *)text);
        
        sprintf(text, "      %2d:%2d:%2d   ", sTime.Hours, sTime.Minutes, sTime.Seconds);
        LCD_DisplayStringLine(Line1, (uint8_t *)text);
        
        sprintf(text, "      %d-%d-%d-%d   ", sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay);
        LCD_DisplayStringLine(Line2, (uint8_t *)text);
}

闹钟中断

char text[20];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
uint8_t led_mode;
void lcd_show()
{
        HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
        sprintf(text, "      test          ");
        LCD_DisplayStringLine(Line0, (uint8_t *)text);
        
        sprintf(text, "      %2d:%2d:%2d   ", sTime.Hours, sTime.Minutes, sTime.Seconds);
        LCD_DisplayStringLine(Line1, (uint8_t *)text);
        
        sprintf(text, "      %d-%d-%d-%d   ", sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay);
        LCD_DisplayStringLine(Line2, (uint8_t *)text);
        
        led_show(1, led_mode);
}

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
        led_mode = 1;
}
感谢观看,不足之处请指正~

评论

  1. AM
    Android Chrome
    8 月前
    2026-3-16 19:35:50

    壁纸选的很有品味

    • 博主
      AM
      Windows Chrome
      8 月前
      2026-3-16 20:03:38

      包的,我的审美再别说😄

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇