第十二届省赛
按键代码
——自认为比较巧妙,定时器3被设置为10ms进入一次中断,代替了HAL_Delay(10)的方法消抖;
运用状态机机思想实现检测多个按键检测——且分为两个状态,其中一个状态PB1和PB2的按键不可使用
struct Key key[4] = {0, 0, 0};
uint8_t f;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
key[0].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
key[2].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
key[3].Key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
key[1].Key_sta = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
}//注意B2和B3对应Key[2]和Key[3];而B4对应Key[1];
// Note that B2 and B3 correspond to Key[2] and Key[3]; B4 corresponds to Key[1];
if (Para_flag==0) f=2;
else f=4;
//让按键B2~B3不可以在界面一中使用
//注意上面的赋值
// Make keys B2 to B3 unavailable in interface 1
// Note the above assignment
for (int i=0; i<f; i++)
{
switch (key[i].sta)
{
case 0:
if (key[i].Key_sta==0)
{
key[i].sta=1;
}
break;
case 1:
if (key[i].Key_sta==0)
{
key[i].sta=2;
}
else
{
key[i].sta=0;
}
break;
case 2:
if(key[i].Key_sta==1)
{
key[i].sta=0;
if(key[i].time<100)
{
key[i].flag=1;
key[i].time=0;
}
}
else
{
key[i].time++;
if (key[i].time>=100)
{
key[i].long_flag=1;
key[i].time=0;
}
}
break;
}
}
}
//注意Time是用于检测长按按键的,本体用不上
菜单函数
在菜单中实现了按键二次检测 伏笔回收
#include "menu.h"
uint8_t data1=2, data2=4, data3=2;
double data4=3.5, data5=2;
char text1[30] = " ", text2[30] = " ",text3[30] = " ", text4[30] = " ";
char flag_m=0,Para_flag=0,PWM_Flag=1;
void Menu(void)
{
if (key[0].flag==1)
{
flag_m=!flag_m;
LCD_Clear(Black);//每次展示菜单前先清屏
key[0].flag=0;
}
if (key[1].flag==1)
{
PWM_Flag=!PWM_Flag;
key[1].long_flag=0;
key[1].flag=0;
}
//PWM和低电平的设置
if (PWM_Flag==0) __HAL_TIM_SetCompare(&htim15, TIM_CHANNEL_1, 20);
else __HAL_TIM_SetCompare(&htim15, TIM_CHANNEL_1, 0);
if (flag_m==0)
{
Para_flag = 0;
show_Data();
}
else
{
Para_flag = 1;
show_Para();
}
}
void show_Data(void)
{
data3=8-data1-data2;
sprintf(text1, " Data ");
sprintf(text2, " CNBR:%d ",data1);
sprintf(text3, " VNBR:%d ",data2);
sprintf(text4, " IDLE:%d ",data3);
LCD_DisplayStringLine(Line2, (unsigned char *)text1);
LCD_DisplayStringLine(Line4, (unsigned char *)text2);
LCD_DisplayStringLine(Line6, (unsigned char *)text3);
LCD_DisplayStringLine(Line8, (unsigned char *)text4);
}
void show_Para(void)
{
sprintf(text1, " Para ");
sprintf(text2, " CNBR:%.2f ",data4);
sprintf(text3, " VNBR:%.2f ",data5);
LCD_DisplayStringLine(Line2, (unsigned char *)text1);
LCD_DisplayStringLine(Line4, (unsigned char *)text2);
LCD_DisplayStringLine(Line6, (unsigned char *)text3);
if (key[2].flag==1)
{
data4+=0.5;
data5+=0.5;
key[2].long_flag=0;
key[2].flag=0;
}
if (key[3].flag==1)
{
data4-=0.5;
data5-=0.5;
key[3].long_flag=0;
key[3].flag=0;
}
}
我认为最难的就是串口部分
也可能是我的方法过于难了,应该可以使用先把串口数据存到数组,然后在数组中依靠下标来检测错误
遇到的难题--就是串口接收时检测到数据不符合格式,虽然重新开始存入数据,但是数据缓存区的数据没有被清空,会被带到下一次数据中,因此在寻找清空缓存区的方法过程中浪费很多时间,所幸也找到了对应的方法--暂时关闭中断接收,再在while循环中打开
//printf函数重写
int fputc(int c, FILE *stream)
{
uint8_t ch[1]={c};
HAL_UART_Transmit(&huart1, (unsigned char *)ch, 1, 0xFFFF);
return c;
}
uint8_t rx=0;
uint8_t Index=0;
char temp_t[5];
char temp_n[5];
char temp_time[11];
char temp_minute[3];
char trash;
uint8_t usart_state=0;//用于标志是否接收完数据 是否可以进入处理函数
//方法1 在串口处初步处理信息——出现bug是无法清除串口缓存区导致的
//串口接收中断函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
if (Index<4)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
temp_t[Index++]=rx;
}
else if(Index==4)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
if (rx!=':')
{
//目的:出现错误时会打印出两个Error,尝试在出现错误后读取数据并清空数据来解决问题
//方法一:失败——找不到对应的寄存器
//把剩下的数据读出即可
//实现方法while(sr&rx_flag)xxx=dr
// while(USART1->ISR&0x10)
// {
// HAL_UART_Receive_IT(&huart1, &rx, 1);
// trash=rx;
// }
//方法二:这个函数似乎不起作用
// __HAL_UART_FLUSH_DRREGISTER(&huart1);
// //方法三:似乎也不起作用
// while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)==SET)
// {
// HAL_UART_Receive_IT(&huart1, &rx, 1);
// }
// __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);
//方法三:直接在这关闭串口中断接收,然后在while循环中再打开 效果出奇地好
//可能关闭中断接收后,对应的缓存区的数据也被清除了
HAL_UART_AbortReceive_IT(&huart1);
Index=0;
printf("Erorr\r\n");
}
else
{
Index++;
}
}
else if (Index<9)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
temp_n[(Index++)-5]=rx;
}
else if (Index==9)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
if (rx!=':')
{
// while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE)==SET)
// {
// HAL_UART_Receive_IT(&huart1, &rx, 1);
// }
// __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_RXNE);
HAL_UART_AbortReceive_IT(&huart1);
Index=0;
printf("Erorr\r\n");
}
else
{
Index++;
}
}
else if (Index>9)//前大段数字
{
if(Index<20)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
temp_time[(Index++)-10]=rx;
}
else//只有分
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
temp_minute[(Index++)-20] = rx;
}
}
if (Index==22)
{
usart_state=1;
Index=0;//表示一组数据接收完成 置标志位 可以进入数据处理函数
}
// if (Index==22)
// {
// for (int i=0;i<4;i++) printf("%c", temp_t[i]);
// printf(":");
// for (int i=0;i<4;i++) printf("%c", temp_n[i]);
// printf(":");
// for (int i=0;i<12;i++) printf("%c", temp_time[i]);
// }
}
}
#include "Myusart.h"
struct In in[8]={0};
uint8_t yn=0;//是否在车库内
uint8_t outcar_index=0;
double Money=0;
uint16_t Time_parking=0;
struct Time temp_time_struct;
void USART_Deal(void)
{
if (usart_state==1)
{
//置零以便下次工作
usart_state=0;
//先把字符串数组的最后一位置为'\0'——必要操作
temp_n[4]='\0';
temp_t[4]='\0';
temp_time[10]='\0';
temp_minute[2]='\0';
//对时间进行处理
//注意strtol最大只能转int32_t的数
uint64_t number=strtol(temp_time, NULL, 10);//字符串数字转int类型
uint8_t number1=strtol(temp_minute, NULL, 10);
temp_time_struct.Y=number/1000000;//year
temp_time_struct.M=number/10000%100;//month
temp_time_struct.D=number/100%100;//day
temp_time_struct.h=number%100;//hour
temp_time_struct.m=number1;//min
//Error情况
if ((temp_time_struct.M>12)&& (temp_time_struct.D>31)&& (temp_time_struct.h>23)&& (temp_time_struct.m>59))
{
printf("Erorr\r\n");
return ;
}
//遍历结构体数组寻找number1;//min是否已经停在车库
for(int i=0; i<8; i++)
{
if (in[i].YN==1)
{
if (!strcmp(temp_n, in[i].num))
{
outcar_index=i;
yn=1;
//出停车库
//并把对应车辆所有清零
}
else {yn=0;}
}
else {yn=0;}
}
//是否在车库内之后的操作
if (yn==1)
{
//出停车库
//并把对应车辆所有清零
in[outcar_index].YN=0;
Time_parking = (temp_time_struct.Y-in[outcar_index].t.Y)*8760
+(temp_time_struct.M-in[outcar_index].t.M)*720
+(temp_time_struct.D-in[outcar_index].t.D)*24
+(temp_time_struct.h-in[outcar_index].t.h)
+(bool)(temp_time_struct.m-in[outcar_index].t.m);
if(!(strcmp(temp_t, "CNBR\0")))
{
Money=Time_parking*data4;
data1--;
}
if ((!strcmp(temp_t, "VNBR\0")))
{
Money=Time_parking*data5;
data2--;
}
printf("%s:%s:%d:%.2f\r\n", temp_t, temp_n, Time_parking, Money);
printf("%d\r\n", in[outcar_index].YN);
}
else//进入停车库 记录信息
{
//判断车位是否已满 如果已满则取消操作
data3=8-data1-data2;
if ((data3-1)<0)
{
printf("Erorr\r\n");
return ;
}
else
{
if(!(strcmp(temp_t, "CNBR\0")))
{
data1++;
}
if ((!strcmp(temp_t, "VNBR\0")))
{
data2++;
}
}
//寻找结构体数组中的空位
uint8_t temp_index=0;
for(int i=0; i<8; i++)
{
if (in[i].YN==0)
{
temp_index=i;
}
}
//赋值
strcpy(in[temp_index].type, temp_t);
strcpy(in[temp_index].num, temp_n);
//注意strtol最大只能转int32_t的数
uint64_t number=strtol(temp_time, NULL, 10);//字符串数字转int类型
uint8_t number1=strtol(temp_minute, NULL, 10);
in[temp_index].t.Y=number/1000000;//year
in[temp_index].t.M=number/10000%100;//month
in[temp_index].t.D=number/100%100;//day
in[temp_index].t.h=number%100;//hour
in[temp_index].t.m=number1;//min
in[temp_index].YN=1;
printf("%s-",in[temp_index].type);
printf("%s-",in[temp_index].num);
printf("%d-%d-%d-%d-%d\r\n",in[temp_index].t.Y,in[temp_index].t.M,in[temp_index].t.D,in[temp_index].t.h,in[temp_index].t.m);
}
}
}
串口代码的优化
这次采取的方法是先把数据存到数组中,再根据下标判断数据是否符合规范
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
HAL_UART_Receive_IT(&huart1, &rx, 1);
data[data_index++] = rx;
if (data_index>=22)
{
//关闭串口接收中断——清空缓存区,以免有多余信息
HAL_UART_AbortReceive_IT(&huart1);
data_index=0;
usart_state=1;
// printf("%s", data);
}
}
}
void Usart_Deal_Better(void)
{
if (usart_state==1)
{
//置零以便下次工作
usart_state=0;
if (!(data[3]==':')&&!(data[9]==':'))//判断':'格式
{
printf("Error");
return ;
}
//判断数字是否正确
strncpy(temp_time, data+10, 10);
strncpy(temp_minute, data+20, 2);
temp_time[10]='\0';
temp_minute[2]='\0';
uint64_t number=strtol(temp_time, NULL, 10);//字符串数字转int类型
uint8_t number1=strtol(temp_minute, NULL, 10);
temp_time_struct.Y=number/1000000;//year
temp_time_struct.M=number/10000%100;//month
temp_time_struct.D=number/100%100;//day
temp_time_struct.h=number%100;//hour
temp_time_struct.m=number1;//min
if ((temp_time_struct.Y>2099)||(temp_time_struct.Y<2000)||(temp_time_struct.M>12)||(temp_time_struct.D>31)||(temp_time_struct.h>23)||(temp_time_struct.m>59))
{
printf("Error\r\n");
return ;
}
//判断是否在停车场中
//复制编号出来
strncpy(temp_n, data+5, 4);
for (int i=0; i<8; i++)
{
if (in[i].YN==1)
{
if (!strcmp(temp_n, in[i].num))//如果在停车场中
{
if (strncmp(data, in[i].type, 4))
{
printf("Error\r\n");
return ;
}
else
{
outcar_index=i;
yn=1;
break;
}
}
else//不在停车场中
{
yn=0;
}
}
else yn=0;
}
uint8_t temp_index=0;//用于寻找结构体数组内的空位
if (yn==0)//新来的
{
if (strncmp(data,"CNBR", 4)&&strncmp(data,"VNBR", 4))//判断停车类型
{
printf("Error\r\n");
return ;
}
else//类型输入正确
{
data3=8-data1-data2;
if ((data3-1)<=0)
{
printf("Error\r\n");
return ;
}
else//停车位充足
{
if (!strncmp(data,"CNBR", 4))
{
data1++;
}
if (!strncmp(data,"VNBR", 4))
{
data2++;
}
for (int i=0; i<8; i++)//寻找结构体数组中的空位
{
if (in[i].YN==0)
{
temp_index=i;
break;
}
}
//登记信息
strncpy(in[temp_index].type, data, 4);
strncpy(in[temp_index].num, data+5, 4);
strncpy(temp_time, data+10, 10);
strncpy(temp_minute, data+20, 2);
temp_time[10]='\0';
temp_minute[2]='\0';
uint64_t number=strtol(temp_time, NULL, 10);//字符串数字转int类型
uint8_t number1=strtol(temp_minute, NULL, 10);
in[temp_index].t.Y=temp_time_struct.Y;
in[temp_index].t.M=temp_time_struct.M;
in[temp_index].t.D=temp_time_struct.D;
in[temp_index].t.h=temp_time_struct.h;
in[temp_index].t.m=temp_time_struct.m;
in[temp_index].YN=1;
printf("%s-",in[temp_index].type);
printf("%s-",in[temp_index].num);
printf("%d-%d-%d-%d-%d\r\n",in[temp_index].t.Y,in[temp_index].t.M,in[temp_index].t.D,in[temp_index].t.h,in[temp_index].t.m);
}
}
}
else//出停车库
{
in[outcar_index].YN=0;
strncpy(temp_t, data, 4);
Time_parking = (temp_time_struct.Y-in[outcar_index].t.Y)*8760
+(temp_time_struct.M-in[outcar_index].t.M)*720
+(temp_time_struct.D-in[outcar_index].t.D)*24
+(temp_time_struct.h-in[outcar_index].t.h);
if ((temp_time_struct.m-in[outcar_index].t.m>0))
{
Time_parking++;
}
if(!(strcmp(temp_t, "CNBR\0")))
{
Money=Time_parking*data4;
data1--;
}
if ((!strcmp(temp_t, "VNBR\0")))
{
Money=Time_parking*data5;
data2--;
}
printf("%s:%s:%d:%.2f\r\n", temp_t, temp_n, Time_parking, Money);
}
}
}
十三届省赛
IIC部分
IIC的读写eeprom和读读eeprom之间必须要有时间间隔,否则会出现写入错误或者读出错误
例如图中的Delay10ms
ADC采样部分
由于之前ADC采集两个电位器电压是用了不同的两个ADC,而这次题目要求是用同一个ADC的不同通道,这就涉及到了ADC多通道采集的问题了
首先cubemx配置,打开对应的输入引脚
再打开扫描模式,连续转换,DMA转运
再
代码甚至只需要一句
自己创建一个数组存储数据
HAL_ADC_Start_DMA(&hadc2, (uint32_t *)ADC_value,2);
仔细看芯片手册,板子上的电压采集是J11和J12口,因为没看清楚,导致浪费了3个小时检查错误
且要注意一直打开DMA转运会严重拖慢程序的执行,即导致板子上程序运行极为缓慢,所以可以在交互后需要获取ADC的值时打开ADC_DMA转运,接收好数据后立马关闭,这样就不会太大地影响板子的运行速度了
if (B4==0)//Data
{
HAL_ADC_Start_DMA(&hadc2, (uint32_t *)ADC_value,2);
HAL_Delay(10);
data1=ADC_value[0]*3.3/4096;
data2=ADC_value[1]*3.3/4096;
recd1++;
recd1_5++;
recdPA4_buff[PA4_Index++]=data1;
recdPA5_buff[PA5_Index++]=data2;
if (recd2<data1) {recd2=data1;}
if (recd3>data1) {recd3=data1;}
if (recd2_5<data2) {recd2_5=data2;}
if (recd3_5>data2) {recd3_5=data2;}
HAL_ADC_Stop_DMA(&hadc2);//记得关闭DMA,否则会导致程序运行缓慢
}