##这道题困了我很久,因为串口接收一直就是出些小bug。
题目:
硬件平台
CT117E/蓝桥杯嵌入式竞赛实训平台
困难程度
困难
题干正文
采集显示与通讯
微控制器:STM32G4系列ARM Cortex M4微控制器
硬件平台:CT117E-M4嵌入式竞赛实训平台
一、设计背景
模拟量输出常见于光照度、温度、压力等传感器,这些传感器能够将物理量转换为连续变化的电信号(电压或电流),微控制器通过AD转换单元,将模拟量转换为数字量,并通过计算获得传感器真实数据。
增加AT指令查询、设置功能。
二、考察内容
(1)微控制器编程基础
(2)AD转换单元驱动设计
(3)LCD屏幕驱动设计
(4)简单的数据统计与逻辑处理
(5)USART接口驱动设计
(6)AT指令处理
三、设计要求
请在 80MHz 系统主频下完成本试题的全部要求。
使用微控制器自带的ADC功能,采集一个传感器输出的模拟信号V,数据采样周期设置为100ms。
传感器输出模拟信号单位为伏特,保留小数点后1位有效数字,连续采样并计算得到传感器输出信号的实时值(V)、最大值(Vmax)、最小值(Vmin)和输出变化趋势(Vx)。将传感器输出的实时值V、Vmax、Vmin和Vx显示到2.4' 16位并口LCD上。
图1 液晶显示界面
变化趋势(Vx)使用符号表示,>>表示上升趋势,<<表示下降趋势。
增加通信功能,支持以下串口AT指令,串口通信波特率设定为9600bps:
① AT
发送:AT\r\n
返回:AT\r\n
② AT+V
发送: AT+V[0..2]\r\n
返回:+V[0..2]: DATA\r\n
参数0..2分别代表实时值、最大值和最小值,DATA为电压数据,保留小数点后2位。
个人理解部分
一.吐槽部分
对于这个题目,它的描述真的是很不准确,不知道它到底是想要传输什么,返回什么。必须是先跑一遍题目后你才知道稍微具体的信息。(困难的原因有一部分就是这个意思。)最难顶的就是,我在电脑测试的好好的,但是那个测试平台就是说无法通过部分。 (希望有懂的小伙伴说一下)
或者是它的hal库可能还没有更新,这里我使用的是串口空闲中断获取字符串并且进入空闲中断处理
各位,我执行的部分已经是满足题目要求了,可是那个测试平台就是说我返回的为NULL 。
所以感觉是它们那里有些bug。
二.对于代码部分的框架
1.我在这里使用的是: 1. hadc2 single-channel 15 也就是我只获取R37的模拟电压值。
2. TIM2定时中断 TIM2 --- ADC2 搭配使用 每100ms进入电压采集
TIM2 配置 (psc: 8000 - 1 , arr: 1000 - 1 反正就是让频率可以达到每100ms进入一次中断 )
3. USART1 串口1 全局中断 bads: 9600 其他默认
4. LCD显示 使用阻断式 轮询执行。
1.先初始化必要的全局变量值 ----> 2.分别在定时器中断中写逻辑执行部分 ----->
3.串口空闲中断中写字符串判断部分 ----> 4.最后是初始化一些必要的执行部分 ------>
5.最后是LCD显示执行。
三.代码部分
main.c
/*
这里是对于ADC取值部分需要的一些变量值
*/
volatile uint16_t raw = 0; //用来接收R37的模拟电压值
float V_Current = 0.0f,V_Max = 0.0f;
float V_Min = 3.3; // 先给一个最大值,防止后面一直显示0
const char* trend_str[2] = {">>", "<<"};
const char* show; //这两个是用于符号改变的,可以看看题目要求就明白了
/* LCD显示部分需要的变量 */
char text[20];
/*UART串口接收需要的变量*/
uint8_t Uart_ReadCache[128];
/*ADC + TIM2定时中断部分 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t choose = 1; //关系到V_Current V_Max V_Min初始化
static float pre_v = 0; // 记录上一次的值
if(htim->Instance == TIM2) //判断是否为tim2
{
HAL_ADC_Start(&hadc2);
raw = HAL_ADC_GetValue(&hadc2);
/* 一定要有 HAL_ADC_PollForConversion() 这个函数等待hadc正确接收完,
如果没有这个函数,就会导致V_Min 一直显示为0 */
if (HAL_ADC_PollForConversion(&hadc2, 20) == HAL_OK)
V_Current = (raw * 3.3f) / 4096; //计算获取值
if(choose)
{
V_Max = V_Min = V_Current;
choose = 0;
}
else
{
V_Max = fmax(V_Current,V_Max);
V_Min = fmin(V_Current,V_Min); //这里进行数值比较
}
float delt = V_Current - pre_v; //获取差值
if(fabsf(delt) < 0.05f)
{
; //不做处理保持上一次的值
}
else
{
trend = (delt > 0) ? 1 : 0;
}
pre_v = V_Current;
//判断趋势
}
}
/* UART串口空闲中断 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart == &huart1)
{
if(strcmp((char*)Uart_ReadCache,"AT\r\n") == 0)
{
WS_Debug("AT");
}
else if(strcmp((char*)Uart_ReadCache,"AT+V[0]") == 0)
{
WS_Debug("+V[0]:%.1f\r\n",V_Current);
}
else if(strcmp((char*)Uart_ReadCache,"AT+V[1]") == 0)
{
WS_Debug("+V[1]:%.1f\r\n",V_Max);
}
else if(strcmp((char*)Uart_ReadCache,"AT+V[2]") == 0)
{
WS_Debug("+V[2]:%.1f\r\n",V_Min);
}
HAL_UARTEx_ReceiveToIdle_IT(&huart1,Uart_ReadCache,128);
/* 一定要再次开启上面的空闲中断,否则只能执行一次 */
}
}
int main(void)
{
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White); //字体设置为白色
/*初始化的时候先开启一次空闲中断*/
HAL_UARTEx_ReceiveToIdle_IT(&huart1,Uart_ReadCache,128);
HAL_ADC_Start(&hadc2);
HAL_TIM_Base_Start_IT(&htim2); //一定要开启
while(1)
{
sprintf(text," DATA");
LCD_DisplayStringLine(Line1,(uint8_t *)text);
sprintf(text," V:%.1f",V_Current);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text," Vmax:%.1f",V_Max);
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text," Vmin:%.1f",V_Min);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
if(trend)
{
sprintf(text," Vx:>>");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
}
else
{
sprintf(text," Vx:<<");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
}
HAL_Delay(50); //稍微延迟一下,不让它显示太快
}
}
对于WS_Debug 那个重定义部分
extern UART_HandleTypeDef huart1;
char formatBuf[64];
// 记得初始化 PA10 PA9 串口引脚
void WS_Debug(char *p,...)
{
va_list ap;
va_start(ap,p);
vsprintf(formatBuf,p,ap);
va_end(ap);
#ifdef DEBUG_EN
HAL_UART_Transmit_IT(&huart1,(uint8_t *)formatBuf,strlen(formatBuf));
#endif
}
对于那个 #ifdef DEBUG_EN #endif
我是在我自己定义的功能函数文件.c 它的头文件定义了一个 #define DEBUG_EN 1
各位也可以自己在.c文件中定义一个#define DEBUG_EN 1
以上就是总体代码部分。