目录
一、情况说明
本次实验代码只有重要部分,并非完整代码,也并未呈现LCD、LED等。将从按键部分、串口输出接收部分、结构体数组储存赋值调取部分来展开;
二、Cube_MX配置设置
1.首先查看电路原理图:
2.配置Cube_MX:
1.按照原理图,配置端口功能:(3KEY+LED1+LED3)
2.设置sys:
3.选择高速时钟:(CCR)
4.对USART1进行设置:
5.设置下载格式:
6.选择Code,下载最必要的库,减小内存占用
三、代码详情
1.按键消抖:
当车库内没有空余车位时,按下KEY1键,蜂鸣器叫一声(按一声叫一声);按下KEY2键,表示即将有车进入车库,通过串口发送ID号(字符+数字),并设置费率,进行储存;按下KEY3键,表示即将有车辆出车库,通过串口输出ID号,显示储存的ID号以及费率;
#include "gpio.h"
#include "stdio.h"
#define KEY1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)
#define KEY2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6)
#define KEY3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5)
#define KEY4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)
#define KEY1_OUT 1
#define KEY2_OUT 2
#define KEY3_OUT 3
int num=0;//现存车辆总数
uint8_t KEY_Scan(uint8_t mode)//按键消抖扫描
{
static uint8_t key_up=1;
if(mode) key_up=1;
if(key_up&&(KEY1==0||KEY2==0||KEY3==0))
{
HAL_Delay(10);
key_up=0;
if(KEY1==0) return KEY1_OUT;
else if(KEY2==0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);//车进入++;
HAL_Delay(100);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);//按键闪烁,确定按下
num++;
return KEY2_OUT;
}
else if(KEY3==0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);//车出去--;
HAL_Delay(100);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
num--;
return KEY3_OUT;
}
}
else if(KEY1==1||KEY2==1||KEY3==1) key_up=1;
return 0;
}
通过num的数量,确定灯应该处于什么样子的状态:(当车库无空余车位时,按下进入键,显示没有停车位;在停车场无车辆时,按下车辆出去键时,显示error)
void check_car()//检查是否有空车位(LED1/LED3)
{
while(1)
{
if(num>10)
{
printf("sorry,have no car parking!");
num=10;
car_flag=1;
}
else if(num<0)
{
printf("error!");
num=0;
car_flag=1;
}
else break;
}
}
2.串口通信及结构体数组储存
HAL库串口通信最大的优势是可以直接接收字符串,不是一个一个字符接收,利用fputc及fgetc直接用printf及scanf接收,这部分代码太简单就不放了。下面是最核心的串口接收后结构体储存函数:
struct car_message
{
char id[5];
char price[20];
int on_flag;//结构体是否为空数据状态
};
/***lnt on_flag便于mian.c里面判断结构体是否为空***/
extern int num;
float price;
struct car_message str[10];
struct car_message *p=str;//指向str结构体首地址
char now_id[5],now_price[10],car_out[5];
int ID_flag=0,ID_out_flag=0;
void scanf_message(struct car_message str[])//将所有信息存入结构体变量
{
if(car_flag==0)//正常状态下执行以下指令
{
while(1)//判断信息是否重复,重复应重新输入,直到不重复为止
{
printf("please give ID:");
HAL_Delay(100);
scanf("%s",now_id);
HAL_Delay(100);
printf("%s\n",now_id);
for(int t=0;t<10;t++)//轮询判断给予ID是否重复
{
if(strcmp(now_id,str[t].id)==0)
{
ID_flag=1;
printf("ID already exists,please input again;\n");
break; //存在相同的ID,flag置1,跳出继续判定,重新输入
}
else{ID_flag=0;}
}
if(ID_flag==0)break;//没有存在相同的ID,跳出循环输入
}//已经正确输入ID信息
printf("please price:");
scanf("%s",now_price);//输入费率
for(int a=0;a<10;a++)
{
if(str[a].on_flag==0)//在数组里循环找一个空的结构体存储
{
printf("%d\n",a);//输出储存结构体里的数据PC端直观看到是否储存
memcpy(str[a].id,now_id,strlen(now_id)+1);//复制进入结构体数组
printf("%s\n",str[a].id);
memcpy(str[a].price,now_price,strlen(now_price)+1);
printf("%s\n",str[a].price);
str[a].on_flag=1;//存储数据进结构体时记得将标识符置1,读取数据记得清0;
printf("OK!");//显示储存成功至PC端
break;
}
}
}
else if(car_flag==1) car_flag=0;//异常状态不执行指令,并将异常符号清0,便于下次按下按键
}
car_flag是‘是否正常按下’标识符,因为前面在check函数里面写了有存在按下错误的可能性(可能是多按没有空余车位存在,也可能是车库内没有车辆却按下car_out),为了避免错误状态下的按下执行接收字符等指令,设置car_flag标识符执行正常车辆范围内的接收存储指令,out输出时同理。把输入函数弄懂之后,读取输出函数便很简单了;
scnaf("%s",x)函数:读取串口输入数据时,如果设置的为char类型数组则不需要加地址符,且存储的方式是,数组内一个位置存下一个字符,所有一定要保障接收字符串够大;如果定义为char变量时,则需要加上地址符号(原因:一个变量内存储了一串字符串),查看接收数据也需要使用到指针;
strcmp(A,B)函数:比较字符串函数,当AB两字符串相等时,返回0值,不等时可能返回-1/1(具体要看具体数值),特别特别重要的是!!!strcmp()函数在检测如遇到相同时,必须同时检测到0(空)时,代表检测结束,return 0;如检测到不同字符串时,检测到第一位不同字符即代表结束;
memcpy(A,B,n)函数:将B内的字符串n位存到A里,具体查看百度百科,在char数组类复制时,不用加地址符,如果是char类型变量时,在复制时需要加地址符。strlen(数组arr or &char变量)可直接计算出该字符串的大小,使用时记得调用String.h!!!
我在打这个代码时,因为串口输入的id号为4位,因此数组只定义了4位,而导致strcmp字符串比较函数无法返回正常值,再调整数组位数之后,便可以返回正常值,所以在后面找到空结构体储存的时候连用3个printf,为了便于观看是否储存成功。