一、硬件框图以及要求
二、模块代码与功能实现
2.1、STM32CubeMX配置
2、模块配置与代码
2.2.1、LED模块
将PC8~PC15配置为GPIO_Output;将PD2配置GPIO_Output。LED驱动代码如下:
//LED驱动代码
void LED_Display(uchar led)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, led << 8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
2.2.2、LCD模块
lcd不需要配置,只需将官方给的lcd.c与lcd.h拷贝到自己项目文件夹下,在main函数中使用LCD_Init();初始化即可使用。
2.2.4、键盘模块
将PB0、PB1、PB2、PA0、配置成GPIO_Input,设置为Pull-up上拉模式。在配置定时器TIM4每10ms中断一次,即将预分频系数psc设置为80-1,重装载值设置为10000-1。
使用定时器TIM4来扫描键盘状态,每隔10ms扫描一次;
key.h文件内容
struct keys
{
uchar key_sta;
uchar key_judge;
bool key_flag;
};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
key.c文件内容
struct keys key[4]={0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4)
{
key[0].key_sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_sta = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_sta = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i=0;i<4;i++)
{
switch(key[i].key_judge)
{
case 0:
{
if(key[i].key_sta == 0) key[i].key_judge = 1;
}
break;
case 1:
{
if(key[i].key_sta == 0) key[i].key_judge = 2;
}
break;
case 2:
{
if(key[i].key_sta == 0)
{
key[i].key_judge = 3;
key[i].key_flag = true;
}
}
break;
case 3:
{
if(key[i].key_sta == 1)
{
key[i].key_judge = 0;
}
}
break;
}
}
}
}
2.2.5、PWM
将PPA7分别配置为TIM17_CH1, PSC为80-1;PA7为2kHz;重装载值分别为10000-1和500-1即可产生2kHz的频率。
2.2.6、串口通信
配置PA9与PA10分别为USART1_Tx与USART1_Rx,波特率9600,打开中断。
串口接收函数,使用void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)回调函数接收信息。
uint8_t rx;
uint8_t rx_data[50];
uint8_t pt = 0;
bool uart_flag = false;
//接收数据,每次只能接收1个字节
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_IT(&huart1,&rx,1);
rx_data[pt++] = rx;
}
//判断是否接收完成
void usart_proc()
{
if(pt != 0)
{
uint8_t temp = pt;
HAL_Delay(5);
if(temp == pt)
{
if(pt > 0)
{
uart_flag = true;
}
}
}
}
三、功能实现
3.1、LCD显示界面
1) 车位显示界面
在车位显示界面下,通过LCD显示界面名称(Data).停车场内目前的停车数量和空闲车位,CNBR 和VNBR代表两类不同的停车类型。
图2所示停车数量共6辆,CNBR类2辆,VNBR类4辆,空闲车位2个。
2) 费率设置界面
在费率设置界面下,通过LCD显示界面名称(Para). CNBR类型和VNBR类型停车的费率,单位为元/小时,保留小数点后2位有效数字。
图3所示CNBR类停车费率位3.50元/小时,VNBR类型停车费率位2.00元/小时。
3) LCD通用显示要求
●显示背景 色(BackColor):黑色
●显示前景色(TextColor): 白色
●请严格按照图示2.3要求设计各个信息项的名称(区分字母大小写)和行列位置。
void show(void)
{
char text[25];
if(view == 0)
{
sprintf(text," Data ");
LCD_DisplayStringLine(Line1,(uint8_t *)text);
sprintf(text," CNBR: %d ",CNBR);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text," VNBR: %d ",VNBR);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
sprintf(text," IDLE: %d ",IDLE);
LCD_DisplayStringLine(Line7,(uint8_t *)text);
}
else//设置界面
{
sprintf(text," Para ");
LCD_DisplayStringLine(Line1,(uint8_t *)text);
sprintf(text," CNBR: %.2f ",Price_CNBR);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text," VNBR: %.2f ",Price_VNBR);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
}
}
3.2、按键功能
1) B1:定义为“界面切换”按键,切换LCD显示“车位显示界面”和“费率设置界面”。
2) B2:定义为 “加”按键,每次按下B2按键,CNBR、VNBR费率增加0.5元。
3) B3:定义为 “减”按键,每次按下B3按键,CNBR、VNBR费率减少0.5元。
4) B4:定义为 “控制”按键,按下后,切换PA7端口输出状态(2KHz,20%占 空比的脉冲信号或持续低电平),切换要求如图4所示。
5) 通用按键设计要求
●按键应进行有效的防抖处理, 避免出现一次按下、多次触发等情形。
●按键B2、 B3仅在费率设置界面有效。
void key_proc(void)
{
if(key[0].key_flag)
{
key[0].key_flag = false;
view = !view;
LCD_Clear(Black);
}
if(view == 1)
{
if(key[1].key_flag)
{
key[1].key_flag = false;
Price_CNBR += 0.5;
Price_VNBR += 0.5;
}
if(key[2].key_flag)
{
key[2].key_flag = false;
Price_CNBR -= 0.5;
if(Price_CNBR <= 0.5) Price_CNBR = 0.5;
Price_VNBR -= 0.5;
if(Price_VNBR <= 0.0) Price_VNBR = 0.0;
}
}
if(key[3].key_flag)
{
key[3].key_flag = false;
PA7 = !PA7;
if(PA7)
{
int arr = 1000000/2000;
int crr = 20*arr/100;
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,crr);
led = led | LED2;
}
else
{
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,0);
led = led & (~LED2);
}
LED_Display(led);
}
}
3.3、串口功能
void handle_proc(void)
{
if(uart_flag)
{
uart_flag = false;
if(pt == 22)//判断信息是否正确
{
char text[30];
sscanf((char*)rx_data,"%4s:%4s:%12s",car_type,car_id,car_time);//分离数据
bool flag = false;
//判断数据库中是否有该车辆的数据,有则表示出停车场,否则进停车场
int i = 0;
for(;i<CAR_NUM;i++)
{
if(car[i].flag&&(strcmp(car_id,car[i].id) == 0))//有则表示出停车场
{
flag = true;//
break;
}
}
if(flag)//找到车辆信息
{
char in_year[3],in_month[3],in_day[3],in_hour[3],in_min[3],in_sec[3];
char out_year[3],out_month[3],out_day[3],out_hour[3],out_min[3],out_sec[3];
//提出时间
sscanf(car[i].time,"%2s%2s%2s%2s%2s%2s",in_year,in_month,in_day,in_hour,in_min,in_sec);
sscanf(car_time,"%2s%2s%2s%2s%2s%2s",out_year,out_month,out_day,out_hour,out_min,out_sec);
sprintf(text,"in : %s:%s:%s\r\n",car[i].type,car[i].id,car[i].time);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
HAL_Delay(10);
sprintf(text,"out: %s:%s:%s\r\n",car_type,car_id,car_time);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
HAL_Delay(10);
//分析及处理
int time = 0;
double rmb = 0;
//将字符串转成数字
int day_in = atoi(in_day);
int hour_in = atoi(in_hour);
int min_in = atoi(in_min);
int day_out = atoi(out_day);
int hour_out = atoi(out_hour);
int min_out = atoi(out_min);
time = (day_out - day_in)*24 + (hour_out - hour_in);
if(min_out - min_in > 0) time++;
if(strcmp(car_type,"CNBR") == 0)
{
CNBR--;
rmb = time*Price_CNBR;
}
else if(strcmp(car_type,"VNBR") == 0)
{
VNBR--;
rmb = time*Price_VNBR;
}
IDLE++;
sprintf(text,"c : %s:%s:%d:%.2f\r\n",car[i].type,car[i].id,time,rmb);
HAL_UART_Transmit(&huart1,(uint8_t*)text,strlen(text),50);
strcpy(car[i].type,"0");
strcpy(car[i].id,"0");
strcpy(car[i].time,"0");
car[i].flag = false;
}
else//进停车场
{
//寻找空位
int i= 0;
flag = false;
for(;i<CAR_NUM;i++)
{
if(car[i].flag == false)//表示车位未被占用
{
flag = true;
break;
}
}
if(flag)
{
//判断车辆类型
if(strcmp(car_type,"CNBR") == 0)
{
if(IDLE > 0)
{
CNBR += 1;
IDLE--;
}
}
else if(strcmp(car_type,"VNBR") == 0)
{
if(IDLE > 0)
{
VNBR += 1;
IDLE--;
}
}
strcpy(car[i].type,car_type);
strcpy(car[i].id,car_id);
strcpy(car[i].time,car_time);
car[i].flag = true;
}
}
}
else
{
HAL_UART_Transmit(&huart1,(uint8_t*)"Erorr\r\n",strlen("Erorr\r\n"),50);
HAL_Delay(5);
}
pt = 0;
memset(rx_data,0,sizeof(rx_data));
}
}
3.5、main函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
HAL_TIM_Base_Start_IT(&htim4);
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,0);//默认占空比为0
HAL_UART_Receive_IT(&huart1,&rx,1);//打开中断
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
LED_Display(0);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
//初始化
for(int i=0;i < CAR_NUM;i++)
{
strcpy(car[i].type,"0");
strcpy(car[i].id,"0");
strcpy(car[i].time,"0");
car[i].flag = false;
}
while (1)
{
key_proc();
show();
usart_proc();
handle_proc();
if(IDLE == 0)
{
led = led & (~LED1);
}
else
{
led = led | LED1;
}
LED_Display(led);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
有不对或不恰当的地方,请您批评指正,主打一个听劝;也可以一起讨论讨论,互相学习,共同进步,比心比心。