2024年4月 蓝桥杯 嵌入式复习资料

本文针对于具有一定HAL库开发基础的人复习,如有侵权,我表示抱歉,请联系作者更改或删除,谢谢。本资料大部分来源于蚂蚁科技资料,已购买!!!!

Day一、LED

LED与LCD会发生冲突,在初始化LCD之后需要关闭LED。单独编写操作LED的函数。

    LCD_Init();//LCD 初始化
	LED_Control(0x00);//关闭LED控制

db4494c02bb44c099daa059488f1f8eb.png

根据此原理图,我们控制LED时需要打开锁存器后才能控制LED,即先打开锁存器(拉高PD2电平),再关闭锁存器 (拉低PD2电平),Q端的数据就可以稳定输出。

设置关于LED 的IO口全为低电平输出模式

1.编写单独的LED控制函数:

#include "led.h"


void LED_Control(u8 led_ctrl)
{
    //先熄灭所有LED灯  //HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);	//让PC8~PC15输出高电平,熄灭LED
    HAL_GPIO_WritePin(GPIOC, 0xff00, GPIO_PIN_SET);         //让PC8~PC15输出高电平,熄灭LED
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);     //打开锁存器
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);   //关闭锁存器

    //根据led_ctrl来点亮对应的LED
    HAL_GPIO_WritePin(GPIOC, led_ctrl << 8, GPIO_PIN_RESET);//根据led_ctrl输出低电平,点亮LED
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);     //打开锁存器
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);   //关闭锁存器
}

使用示例:我的板子是0x55为 0101 0101      0是咩,1时亮,请注意你的板子引脚设置

  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    LED_Control(0xff);//全开
    HAL_Delay(500);	//延时500毫秒
    LED_Control(0x00);//全关
    HAL_Delay(500);	//延时500毫秒
  }

另一种方法更为高效,采用滴答定时器的方式,编写单独的LED函数,在主循环里调用该函数任务即可。

// LED执行程序
__IO uint32_t ledTick = 0; 
u8 led_ctrl = 0xff; 
void LED_Process(void) 
{ 
    if(uwTick - ledTick < 100) return ; //不阻塞状态延时100ms
        ledTick = uwTick; 
    LED_Control(led_ctrl); 
    led_ctrl = ~led_ctrl; 
}

要采用不阻塞状态延时,需要创建一个函数,本质上是满足技术到达设定值时才可运行下面的条件任务,否则直接return退出函数,继续执行其他任务命令

Day二、按键控制

72ca55bc07824065ba660323b02ef4e3.png

根据原理图,按键按下,IO读到低电平,松开按键,IO读到高电平。

按键需要消抖,一般为10ms延时消抖。

设置按键引脚为 浮空输入模式,

代码如下:

1.编写KEY驱动函数

#include "key.h"

#define KB1  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KB3  HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KB4  HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define KEYPORT  KB1 | (KB2<<1) | (KB3<<2) | (KB4<<3) | 0xf0

u8 Trg;      // 全局变量,单次触发
u8 Cont;     // 全局变量,长按
void Key_Read(void)
{
    u8 ReadData = (KEYPORT)^0xff;             // 1 (异或,不同为1)
    Trg = ReadData & (ReadData ^ Cont);       // 2 
    Cont = ReadData;                          // 3
}

 2.编写KEY任务函数

// 按键执行程序
__IO uint32_t keyTick = 0;
u16 key1_val = 0;
u16 cnt_key_time = 0;
void Key_Process(void)
{
    if(uwTick - keyTick < 10) return ;
    keyTick = uwTick; //不阻塞状态 延时10ms

    Key_Read();
    //短按操作
    if(Trg & 0x01)	//B1
    {
        LED_Control(0x01);//第1个LED显示
        key1_val ++;
    }
    if(Trg & 0x02)	//B2
    {
        LED_Control(0x02);//第2个LED显示
    }
    if(Trg & 0x04)	//B3
    {
        LED_Control(0x04);//第3个LED显示
    }
    if(Trg & 0x08)	//B4
    {
        LED_Control(0x08);//第4个LED显示
    }
    

    //下面为处理长按操作:
    if(Cont & 0x01)
    {
         cnt_key_time++;
        if(cnt_key_time == 100)  //连续按下1S
        {
           cnt_key_time =0;
            LED_Control(0xff);//全开
            
        }
    }
}

Day三、ADC

STM32G431内部集成2个12位ADC(ADC1、ADC2)

e64a5c3bf8394ea5b88d6152451e452b.png

根据G431的原理图,我们需要对PB15和PB12进行电压采集,

可以max中设置PB15为ADC2 IN15   ,PB12为ADC1 IN11,均采用(single—ended)单端模式

简单的  开启功能,采集值,转换值

// ADC
u16 adc1_val,adc2_val;
float volt_r37,volt_r38;

void ADC_collect_process(void)
{
    HAL_ADC_Start(&hadc1);              //启动ADC1的功能
    adc1_val = HAL_ADC_GetValue(&hadc1);//采集ADC1的值
    volt_r38 = adc1_val/4095.0f*3.3f;   //数据转换成实际的电压值
    HAL_ADC_Start(&hadc2);              //启动ADC2的功能
    adc2_val = HAL_ADC_GetValue(&hadc2);//采集ADC2的值
    volt_r37 = adc2_val/4095.0f*3.3f;   //数据转换成实际的电压值
}

Day四、LCD液晶屏

    LCD液晶屏的分辨率为320*240 (一行可显示20个字符,显示10行)

    比赛提供的HAL_LCD例程,相关的IO初始化已经初始化完成。

在使用例程时:

单纯的初始化需要注意:

    LCD_Init();             //初始化LCD
    LED_Control(0x00);      //关闭LED
    LCD_Clear(Blue);        //清屏,背景为蓝色
    LCD_SetBackColor(Blue); //设置背景为蓝色
    LCD_SetTextColor(White);//设置文本颜色为白色
    LCD_Process();          //LCD 任务函数
// LCD执行程序
void LCD_Process(void)
{
    u8 display_buf[20];

    //【问题】长数据对短数据覆盖问题
    sprintf((char *)display_buf, "%d", 4000);
    LCD_DisplayStringLine(Line0, display_buf);
    sprintf((char *)display_buf, "%d", 10);
    LCD_DisplayStringLine(Line0, display_buf);

    //--> 解决方案:加空格,针对字符串
    LCD_DisplayStringLine(Line2, "hello");
    LCD_DisplayStringLine(Line2, "hi   ");
    //--> 解决方案:格式化输出,针对数据
    sprintf((char *) display_buf, "%5d", 4000);         //显示5位,默认右对齐
    LCD_DisplayStringLine(Line3, display_buf);
    sprintf((char *) display_buf, "%5d", 10);
    LCD_DisplayStringLine(Line3, display_buf);

    //格式化输出例子
    sprintf((char *) display_buf, "%-5d", 10);          //左对齐
    LCD_DisplayStringLine(Line4, display_buf);

    sprintf((char *) display_buf, "%05d", 500);         //前面补0
    LCD_DisplayStringLine(Line5, display_buf);

    sprintf((char *) display_buf, "%5.3f", 3.1415926);  //显示小数,总长是5位数(小数点算1位),小数点后是2位
    LCD_DisplayStringLine(Line6, display_buf);

    sprintf((char *) display_buf, "%x", 15);            //%x显示16进制,%o显示8进制
    LCD_DisplayStringLine(Line7, display_buf);

    sprintf((char *) display_buf, "%c", 'a');           //%s字符串,%c字符
    LCD_DisplayStringLine(Line8, display_buf);

    sprintf((char *) display_buf, "%d %% ", 10);        //输出百分号:%
    LCD_DisplayStringLine(Line9, display_buf);
}

     使用sprintf函数时,需要引用外部头文件#include "stdio.h",包括串口也会用到此头文件,在数据转换时,要避免数据格式错误,长数据对短数据的覆盖,应在后面加入空格或格式化!!!

Day五;IIC协议的理解与应用

5f723f662d7649c3bdf3aeb69b57063e.png

蓝桥杯开发板用到IIC协议的外设有

AT240C2  EEPROM存储器

MCP4107 AD 芯片  (数字电位器)

IIC 有两条线

           SCL:时钟线

           SDA:数据线

在使用时需要对两条线进行上拉处理

        SCL为高时,SDA必须保持稳定!

         故 SCL为低时,SDA上的电平才可以变化!

        写数据时:SCL为低,改变SDA

        读数据时,SCL为高,读取IO电平

一、IIC协议的使用,EEPROM

1、注意事项

        EEPROM的器件地址:1010   000(R/W)

                           写             0xA0代表单片机向AT24C02写数据

                           读             0xA1代表单片机向AT24C02读数据

写入周期 5ms

2、代码编写

 首先引用官方文件包的I2C.C和.H文件包,因为引脚在文件包中已经配置好,所以我们只需要配置好引用的代码顺序就可以正常使用。

比赛中需要自己编写EEPROM的读写函数,只需要看数据手册的时序进行编写即可,很轻松。

写函数:

//比赛中,需要自己编写EEPROM的读写函数
//写24C02
void EEPROM_Write(u8 add, u8 dat)
{
    I2CStart();
    I2CSendByte(0xa0);
    I2CWaitAck();

    I2CSendByte(add);
    I2CWaitAck();
    I2CSendByte(dat);
    I2CWaitAck();
    I2CStop();
    HAL_Delay(5);
}

读函数:

//读24C02
u8 EEPROM_Read(u8 add)
{
    u8 dat;

    I2CStart();
    I2CSendByte(0xa0);
    I2CWaitAck();
    I2CSendByte(add);
    I2CWaitAck();

    I2CStart();
    I2CSendByte(0xa1);
    I2CWaitAck();
    dat = I2CReceiveByte();
    I2CSendNotAck();
    I2CStop();

    return(dat);
}

然后在头文件中进行函数声明即可。

该代码为显示单片机启动次数

    //EEPROM
    I2CInit();//引用初始化函数
    startup_times = EEPROM_Read(0x20);//读取该地址的数据
    EEPROM_Write(0x20, ++startup_times);//对该地址写入数据
    LCD_Process(); //LCD显示
// EEPROM
u8 val_24c02 = 0;
u8 startup_times = 0;

// LCD执行程序
void LCD_Process(void)
{
    u8 display_buf[20];
    sprintf((char *)display_buf, "%3d", startup_times);//显示当前重启次数
    LCD_DisplayStringLine(Line0, display_buf);
}

二、数字电位器(MCP4017)

1.注意事项

  可通过PB14对电位器输出 的电压进行采样,

       MCP4017的器件地址:0101   111(R/W)

                           写             0x5e 代表单片机向电位器写数据

                           读             0x5f 代表单片机向电位器读数据

协议格式请于数据手册5.4进行查找

读取到的电压值数据计算:参考电压*(设定电阻/(设定电阻+10k)),设定电阻为电位器设定值,

实际阻值:0~100k,

参数值:0~0x7f   

设定阻值计算: 参数值*(100k/127)

就算参数值设置为0,它本身还有0.1k欧姆的阻值

2.代码编写

写 函数:

//写MCP4017
void MCP4017_Write(u8 val)
{
    I2CStart();
    I2CSendByte(0x5E);
    I2CWaitAck();

    I2CSendByte(val);
    I2CWaitAck();
    I2CStop();
}

读 函数:比赛中可用可不用

//读MCP4017
u8 MCP4017_Read(void)
{
    u8 val;
    I2CStart();
    I2CSendByte(0x5F);
    I2CWaitAck();

    val = I2CReceiveByte();
    I2CSendNotAck();
    I2CStop();

    return val;
}

使用示例:

    //MCP4017
    MCP4017_Write(0x70);//写数据
    val_mcp = MCP4017_Read();//读取数据

3.在使用双通道ADC

需在cubemax设定PB14为ADC1 通道5

配置ADC的RANK和采样速度;

并开启轮询采集方式,设定为2  rank,同时也要注意轮询的顺序,和采样时间,可以改为640个时钟周期,保证电压的准确性;

5c4eee13ef334983be709ec1c14f7974.png

代码:

// ADC执行程序
u16 adc1_val, adc2_val;
float volt_r37, volt_r38, volt_mcp;
void ADC_Process(void)
{
    //RANK1 - CH5
    HAL_ADC_Start(&hadc1);
    volt_mcp = HAL_ADC_GetValue(&hadc1) / 4096.0f * 3.3f;
    //RANK2 - CH11
    HAL_ADC_Start(&hadc1);
    adc1_val = HAL_ADC_GetValue(&hadc1);
    volt_r38 = adc1_val / 4096.0f * 3.3f;

    //ADC2的采集
    HAL_ADC_Start(&hadc2);
    adc2_val = HAL_ADC_GetValue(&hadc2);
    volt_r37 = adc2_val / 4096.0f * 3.3f;
}

Day六、DAC

官方原理图

对应的引脚为

ADC1      PA4 -> OU1

                PA5->OUT2

均设置输出到外设。

void Dac1_Set_Vol(float vol)
{
 uint16_t temp;
 temp = (4096*vol/3.3f);
 HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);

}

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桂北研猛男

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值