Advertisement

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);
    
  
    
 }
    
    
    
    

全部评论 (0)

还没有任何评论哟~