整体架构流程
1.外设初始化
大体浏览程序设计可知,我们所需要初始化的外设有:LED、PWM、USART、按键这四个外设的端口,EEPROM有蓝桥杯大赛官方准备的i2C-hal.c和i2C-hal.h文件,实验不需要我们去初始化,初始文件也是采用hal库的液晶驱动程序。
USART我们需要调节成异步模式,波特率为9600bit/s
通过题目可知,我们需要使PA1输出2000Hz的频率,且初始占空比为5%
2.LCD界面显示
void LCD_Process()
{
u8 diaplay_buf[20];//存储显示数据
if(DISPLAY_MODE==DISPLAY_SHOP)
{
LCD_DisplayStringLine(Line2, (uint8_t *)" SHOP ");
sprintf((char *)diaplay_buf," X:%-2d",Xs);
LCD_DisplayStringLine(Line4,(uint8_t *)diaplay_buf);
sprintf((char *)diaplay_buf," Y:%-2d",Ys);
LCD_DisplayStringLine(Line5,(uint8_t *)diaplay_buf);
}
else if(DISPLAY_MODE==DISPLAY_PRICE)
{
LCD_DisplayStringLine(Line2, (uint8_t *)" PRICE ");
sprintf((char *)diaplay_buf," X:%-2.1f",Xv/10.0f);
LCD_DisplayStringLine(Line4,(uint8_t *)diaplay_buf);
sprintf((char *)diaplay_buf," Y:%-2.1f",Yv/10.0f);
LCD_DisplayStringLine(Line5,(uint8_t *)diaplay_buf);
}
else
{
LCD_DisplayStringLine(Line2, (uint8_t *)" REP ");
sprintf((char *)diaplay_buf," X:%-2d",Xn);
LCD_DisplayStringLine(Line4,(uint8_t *)diaplay_buf);
sprintf((char *)diaplay_buf," Y:%-2d",Yn);
LCD_DisplayStringLine(Line5,(uint8_t *)diaplay_buf);
}
}
为了更加简洁方便的显示数据,我们可以通过sprintf()函数将数据写入到一个数组中进行间接的输出
3.按键
void Key_Process()
{
if(uwTick - KEYTICK < 20) return;
KEYTICK=uwTick;
Key_Read();
if(Trg & 0x01)
{
LCD_Clear(Black);
DISPLAY_MODE=(DISPLAY_MODE + 1 ) % 3;
}
if(Trg & 0x02)
{
if(DISPLAY_MODE==DISPLAY_SHOP)
{
Xs=(Xs + 1)%(Xn+1);
}
else if(DISPLAY_MODE==DISPLAY_PRICE)
{
Xv++;
if(Xv==21) {Xv=10;}
}
else if(DISPLAY_MODE==DISPLAY_REP)
{
Xn++;
}
}
if(Trg & 0x04)
{
if(DISPLAY_MODE==DISPLAY_SHOP)
{
Ys=(Ys + 1)%(Yn+1);
}
else if(DISPLAY_MODE==DISPLAY_PRICE)
{
Yv++;
if(Yv==21) {Yv=10;}
}
else if(DISPLAY_MODE==DISPLAY_REP)
{
Yn++;
}
}
if(Trg & 0x08)
{
if(DISPLAY_MODE==DISPLAY_SHOP)
{
if(Xn >= Xs || Yn >= Ys)
{
Xn=Xn-Xs;
Yn=Yn-Ys;
Xs=0;Ys=0;
}
}
}
}
程序设计要求B1可以实现三个届面的切换,所以我们需要定义一个变量ru:DISPLAY_MODE,来表示届面,还需要定义三个parameter常量表示购买界面、单价界面和库存界面;
B2则需要实现商品X在三个界面下的调试,如:在购买界面按一下B2则购买数量加1,在单价界面按一下B2则单价+0.1,在库存界面则是库存数量加1
B3功能和B2一样代表商品Y
B4表示确认购买商品
4.EEPROM的读写
void EEPROM_Process()
{
if(EEPROM_Read(0xf0)!= 0xa0)
{
EEPROM_Write(0xf0,0xa0);
EEPROM_Write(0x00,Xn);
EEPROM_Write(0x01,Yn);
EEPROM_Write(0x02,Xv);
EEPROM_Write(0x03,Yv);
}
Xn = EEPROM_Read(0x00);
Yn = EEPROM_Read(0x01);
Xv = EEPROM_Read(0x02);
Yv = EEPROM_Read(0x03);
}
这是检查EEPROM是否是第一次上电,如果是则写入初始数据,如果不是则读取数据
根据要求可知,我们需要在库存数量和价格在变化的时候写入,不变化的时候不变化,所以我们需要在按键按下去的时候就写入数据,例如:
if(Trg & 0x02)
{
if(DISPLAY_MODE==DISPLAY_SHOP)
{
Xs=(Xs + 1)%(Xn+1);
}
else if(DISPLAY_MODE==DISPLAY_PRICE)
{
Xv++;//单价改变
if(Xv==21) {Xv=10;}
EEPROM_Write(0x02,Xv);
}
else if(DISPLAY_MODE==DISPLAY_REP)
{
Xn++;
EEPROM_Write(0x00,Xn);//库存数量改变
}
}
5.PWM输出
PWM输出只需要改变占空比即可
void PWM_Process()
{
if(led_flag==1)
{
TIM2->CCR2=150;
}
else
TIM2->CCR2=25;
}
6.LED闪烁
void Led_Process()
{
if(led_flag==1)
{
led_Ctrl |=0x01;
cnt_5s++;
if(cnt_5s==5000)
{
cnt_5s=0;
led_flag=0;
led_Ctrl &=~0x01;
}
}
if(Xn==0 && Yn==0)
{
cnt_1 = cnt_1%200 +1;
if(cnt_1 <= 100)
led_Ctrl |=0x02;
else
led_Ctrl&=~0x02;
}
else
led_Ctrl&=~0x02;
}
led1在确认购买商品后亮5秒,即B4按下后亮5秒,我们可以通过systick来计数
led2是X和Y的库存数量为零是以0.1秒为间隔进行闪烁,也可以用systick来计数
void SysTick_Handler(void)//1ms
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
7.USART的发送和接收
int fputc(int ch, FILE *f)//重定向
{
HAL_UART_Transmit(&huart1,(unsigned char *)ch,1,50);
return(ch);
}
u8 uart_buf[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_IT(&huart1,uart_buf,1);
if(uart_buf[0] == '?')
{
printf("X:%.1f,Y:%.1f",Xv/10.0f,Yv/10.0f);
}
}
对串口的发送我们可以进行printf的重定向,这样我们就可以通过printf来发送数据
技术细节
1.在调用void SysTick_Handler(void)时我们需要在stm32xxxxit.c把它屏蔽,否则会发生冲突代码会报错
2.LED的处理函数我们可以放在SysTick_Handler()中,如果直接将LED_Procrss()放在main函数中会和LCD发生冲突,代码是不会报错的,但是液晶显示的时候会用一些明显的小点,控制单个led亮灭的函数放在while中