Hi~,大家好呀,今天给大家分享的是嵌入式国赛第10届的真题详解。这届题目较前后几届难度略大,非常适合我们练手。
好了废话不多说,我们直接进入正题!
首先理清我们需要用到的外设:
可以看到我们分别要完成对以下模块的配置:
其中出现了省赛没有出现的外设:温度传感器ds18b20以及数码管。
先上这两个外设的初始化函数:
int16_t ds18b20_read(void)
{
uint8_t TH;
uint8_t TL;
ow_reset();
ow_byte_wr(OW_SKIP_ROM);
ow_byte_wr(DS18B20_CONVERT);
ow_reset();
ow_byte_wr(OW_SKIP_ROM);
ow_byte_wr(DS18B20_READ);
TL = ow_byte_rd();
TH = ow_byte_rd();
return (((TH << 8) | TL) & 0x7FFF);
}
ds18b20
#include "seg\seg.h"
uint8_t Seg[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x00};
//初始化函数
void Seg_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
//显示函数
void Seg_Display_Value(uint8_t Bit1, uint8_t Bit2, uint8_t Bit3)
{
uint8_t Temp = 0;
uint8_t i = 0;
SCK_L;
RCK_L;
Temp = Seg[Bit3];
for(i = 0; i < 8; i++)
{
if(Temp & (0x80 >> i))
SER_H;
else
SER_L;
SCK_H;
SCK_L;
}
Temp = Seg[Bit2];
for(i = 0; i < 8; i++)
{
if(Temp & (0x80 >> i))
{
SER_H;
}
else
{
SER_L;
}
SCK_H;
SCK_L;
}
Temp = Seg[Bit1];
for(i = 0; i < 8; i++)
{
if(Temp & (0x80 >> i))
{
SER_H;
}
else
{
SER_L;
}
SCK_H;
SCK_L;
}
RCK_H;
}
//头文件引用
#ifndef __SEG_H__
#define __SEG_H__
#include "main.h"
#define SER_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET)
#define SER_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET)
#define RCK_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET)
#define RCK_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET)
#define SCK_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET)
#define SCK_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET)
void Seg_Init(void);
void Seg_Display_Value(uint8_t Bit1, uint8_t Bit2, uint8_t Bit3);
#endif
seg
我总结了一下我在这道题中碰到的难点,以及自己写的有意思的地方,这里分享给大家:难点在于串口的收发部分以及数据的存储部分。
这一年的题使用了大量串口的数据处理,以及对数据有效性的判断:
以及在AT24C02数据存储中需要存储16字节的数据
首先我们对串口部分进行讲解:
接收中断函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(pRx_Buf == 0)
{
Uart_Set_Point = uwTick;
Uart_Flag = 1;
}
if(Uart_Flag == 1)
{
Rx_Buf[pRx_Buf] = Rx_Buffer;
pRx_Buf++;
}
HAL_UART_Receive_IT(&huart1, &Rx_Buffer, 1);
}
我使用了一个字节一个字节接收的方式将数据先缓存在Rx_Buffer中,再将其转入数组,并且在首次接收到数据后进行时间打点,纪录此处的时间Uart_Set_Point。
然后是数据处理函数:
void Uart_Proc(void)
{
if(uwTick - uwTick_Uart_Set_Point < 200) return;
uwTick_Uart_Set_Point = uwTick;
Uart_Count_Point++;
if(Uart_Count_Point > 5 && AO_Flag)
{
printf("&%.2f\r\n", Temp);
Uart_Count_Point = 0;
}
if(uwTick - Uart_Set_Point >= 200 && uwTick - Uart_Set_Point < 400)
{
if(Rx_Buf[0] == 'A' && Rx_Buf[1] == 'T' && Rx_Buf[2] == 0x0D && Rx_Buf[3] == 0x0A)
{
printf("&%.2f\r\n", Temp);
}
else if(Rx_Buf[0] == 'P' && Rx_Buf[1] == 'A' && Rx_Buf[2] == 'R' && Rx_Buf[3] == 'A'
&& Rx_Buf[4] == 0x0D && Rx_Buf[5] == 0x0A)
{
printf("#%d,AO%d\r\n", Temp_Compare_Disp, AO_Seclet_Disp + 1);
}
else
{
printf("no");
}
Uart_Flag = 0;
pRx_Buf = 0;
}
}
在中断函数打点的时间在此函数内处理,将接收到的数据在0.2s左右进行数据的处理与分析。
对EEPROM的处理
因为EEPROM一次只能存储一个字节,那么如果我们想按照题意存储一个0~65535内的数据,该如何操作呢?
为此我定义了一个联合体他们N_16,和N_8[2]指向了同一块空间,即他们共用了这一片16bit的空间范围。
这样只要把N_R.N_8[0],与N_R.N_8[1]分别存储在EEPROM内,然后再进行读取,在使用时直接使用N_R.N_16,就可以优雅的得到我们想要的16位的数据啦。