赛题
一、题目
“双路输出控制器”具有信号输出时间设定、输出信号占空比调整、当前输出通道及时间显示、系统工作参数存储、串口通讯及 LED 指示等功能。
“双路输出控制器”通过串口完成信号输出时间设定功能;通过 EEPROM 完成系统工作参数存储功能;通过按键完成输出通道切换、输出信号占空比调整及停止信号输出功能;
系统硬件电路主要由 MCU 控制单元、独立按键、LCD 显示单元、串口通讯单元、EEPROM 数据存储单元和 LED 指示单元组成,系统框图如图 1 所示:
设计任务及要求
- 独立按键功能
1.1 按下 B1 按键,PA1 输出脉宽调制信号,再次按下 B1,PA1 持续输出低电平,如此循环;
1.2 B2 按键功能设定为通道 PA1 输出脉宽调制信号占空比调整,按下 B2,通道 PA1 输出信号占空比以 10%步进,调整后的输出信号占空比将保存至 EEPROM 中;
1.3 按下 B3 按键,PA2 输出脉宽调制信号,再次按下 B3,PA2 持续输出低电平,如此循环;
1.4 B4 按键功能设定为通道 PA2 输出脉宽调制信号占空比调整,按下 B4,通道 PA2 输出信号占空比以 10%步进,调整后的输出信号占空比将保存至 EEPROM 中。
-
串口通讯单元
系统可通过串口接收命令,用户输入字符串“hh:mm:ss-PAx-yS”,设定 PAx 通道在 hh时 mm 分 ss 秒输出脉宽调制信号,持 续输出 y 秒(0<y<10)。使用 STM32 USART2 完成上述串口功能,并将通讯波特率设定为 9600。 [命令格式举例] - 通过串口输入“00:00:20-PA1-5S”,即设定系统在 0 时 0 分 20 秒通过 PA1 通道持续输出脉宽调制信号,5 秒后输出低电平信号。
-
LCD 显示单元
通过 LCD 显示当前 EEPROM 中存储的脉宽调制信号占空比、系统时间、当前正在输出的通道以及通过串口接收到的命令,LCD 显示界面参考示意图如图 2、图 3 所示:
-
EEPROM 数据存储单元
通过 EEPROM 存储 PA1、PA2 输出信号的占空比,占空比数值可以通过按键调整。
-
LED 指示功能
通道 PA1 输出脉宽调制信号时,指示灯 LD1 点亮,其余指示灯处于熄灭状态;
通道 PA2 输出脉宽调制信号时,指示灯 LD2 点亮,其余指示灯处于熄灭状态。 -
系统工作及初始化状态说明
系统初始化时间设定为 23 时 59 分 50 秒,PA1、PA2 输出频率固定为 1KHz,串口通讯波特率设定为 9600 bps。
串口2与PWM冲突解决方法
根据题目需求,无需串口发送,只需接收即可,故把串口2发送引脚重映射为PA14或其他不为PA2即可。
串口配置这里改为只接收
在自动生成的 HAL_UART_MspInit 函数里,把串口2发送相关配置删除即可,如下:
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* USART2 clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
PA3 ------> USART2_RX
PA14 ------> USART2_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2 DMA Init */
/* USART2_RX Init */
hdma_usart2_rx.Instance = DMA1_Channel1;
hdma_usart2_rx.Init.Request = DMA_REQUEST_USART2_RX;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
}
部分程序
主函数
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM2_Init();
MX_TIM6_Init();
MX_USART2_UART_Init();
MX_RTC_Init();
#if PWM_IN_ON_TIM7
MX_TIM7_Init();
#endif
/* USER CODE BEGIN 2 */
I2CInit();
EEPROM_init();
LED_init();
LCD_Init();
LCD_DisplayStringLine(Line6,LCD_Line6_buf);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
#if PWM_IN_ON
PWM_IN_function();
#endif
KEY_function();
RTC_function();
UART2_function();
LED_function();
LCD_function();
}
按键功能函数
void KEY_function(void)
{
u8 key;
key=KEY_SCAN();
if(key)
{
if(key==1)
{
LCD_Line5_buf[10]=1+48; LCD_flag|=(1<<5);
flag_PA_zbk^=(0x01<<0);
if(flag_PA_zbk&0x01) {LED_state&=0xFE;TIM2->CCR2=PA1_zbk*10;}
else
{
LED_state|=0X01;TIM2->CCR2=0;
if(flag_PA_zbk&0x02) { LCD_Line5_buf[10]=2+48;}
}
}
else if(key==2)
{
if(PA1_zbk+=10,PA1_zbk>90) PA1_zbk=10;
//保存到EEPROM
EEPROM_write(EEPROM_ARR_START+EEPROM_CHECK_LEN+EEPROM_DATA_PA1,PA1_zbk);
if(flag_PA_zbk&0x01) {LED_state&=0xFE;TIM2->CCR2=PA1_zbk*10;}
LCD_Line2_buf[8]=PA1_zbk/10+48;
LCD_Line2_buf[9]=PA1_zbk%10+48;
LCD_flag|=(1<<2);
}
else if(key==3)
{
LCD_Line5_buf[10]=2+48; LCD_flag|=(1<<5);
flag_PA_zbk^=(0x01<<1);
if(flag_PA_zbk&0x02) {LED_state&=0XFD;TIM2->CCR3=PA2_zbk*10;}
else
{
if(flag_PA_zbk&0x01) { LCD_Line5_buf[10]=1+48;}
LED_state|=0X02;TIM2->CCR3=0;
}
}
else if(key==4)
{
if(PA2_zbk+=10,PA2_zbk>90) PA2_zbk=10;
//保存到EEPROM
EEPROM_write(EEPROM_ARR_START+EEPROM_CHECK_LEN+EEPROM_DATA_PA2,PA2_zbk);
if(flag_PA_zbk&0x02) {LED_state&=0XFD;TIM2->CCR3=PA2_zbk*10;}
LCD_Line3_buf[8]=PA2_zbk/10+48;
LCD_Line3_buf[9]=PA2_zbk%10+48;
LCD_flag|=(1<<3);
}
}
}
RTC功能函数
/* USER CODE BEGIN 1 */
void RTC_function(void)
{
static RTC_TimeTypeDef RTC_Time_last;
if(!TASK_TIMES.RTC_time)
{
TASK_TIMES.RTC_time=RTC_TIMES;
HAL_RTC_GetTime(&hrtc,&RTC_Time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&RTC_Date,RTC_FORMAT_BIN);
if(PWM_OUT_DATA[5]==1)
{
if(PWM_OUT_DATA[2]==RTC_Time.Seconds&&PWM_OUT_DATA[1]==RTC_Time.Minutes&&PWM_OUT_DATA[0]==RTC_Time.Hours)
{
PWM_OUT_DATA[5]=2;
if(PWM_OUT_DATA[3]=='1') {flag_PA_zbk|=0X01;LED_state&=0xFE;TIM2->CCR2=PA1_zbk*10;}
else if(PWM_OUT_DATA[3]=='2') {flag_PA_zbk|=0X02;LED_state&=0xFD;TIM2->CCR3=PA2_zbk*10;}
}
}
else if(PWM_OUT_DATA[5]==2)
{
if(PWM_OUT_DATA[4]==RTC_Time.Seconds)
{
PWM_OUT_DATA[5]=0;
if(PWM_OUT_DATA[3]=='1') {flag_PA_zbk&=0XFE;LED_state|=0x01;TIM2->CCR2=0;LCD_Line5_buf[10]=1+48; LCD_flag|=(1<<5);}
else if(PWM_OUT_DATA[3]=='2') {flag_PA_zbk&=0XFD;LED_state|=0x02;TIM2->CCR3=0;LCD_Line5_buf[10]=2+48; LCD_flag|=(1<<5);}
LCD_DisplayStringLine(Line7,LCD_Line7_buf);
}
}
if(RTC_Time_last.Seconds!=RTC_Time.Seconds)
{
RTC_Time_last.Seconds=RTC_Time.Seconds;
LCD_Line4_buf[6]=RTC_Time.Hours/10+48; LCD_Line4_buf[7]=RTC_Time.Hours%10+48;
LCD_Line4_buf[9]=RTC_Time.Minutes/10+48; LCD_Line4_buf[10]=RTC_Time.Minutes%10+48;
LCD_Line4_buf[12]=RTC_Time.Seconds/10+48; LCD_Line4_buf[13]=RTC_Time.Seconds%10+48;
LCD_flag|=(0X01<<4); //刷新时间
}
}
}
/* USER CODE END 1 */
串口功能函数
/* USER CODE BEGIN 1 */
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)==SET)
{
__HAL_UART_CLEAR_FLAG(&huart2,UART_FLAG_IDLE);
HAL_UART_DMAStop(&huart2);
UART2_LEN=50-__HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
UART2_over=1;
}
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
u8 UART2_DATA_check(void)
{
u8 h,d,s;
if(UART2_LEN==15) //长度
{
if(UART2_RX_buf[2]==':'&&UART2_RX_buf[5]==':'&&UART2_RX_buf[8]=='-'&&UART2_RX_buf[12]=='-'&&UART2_RX_buf[9]=='P'&&UART2_RX_buf[10]=='A'&&UART2_RX_buf[14]=='S') //格式
{
if(UART2_RX_buf[0]<'0'||UART2_RX_buf[0]>'2'||UART2_RX_buf[1]<'0'||UART2_RX_buf[1]>'9') return 0;
h=(UART2_RX_buf[0]-48)*10+(UART2_RX_buf[1]-48); //时格式校验
if(h>=24) return 0;
if(UART2_RX_buf[3]<'0'||UART2_RX_buf[3]>'9'||UART2_RX_buf[4]<'0'||UART2_RX_buf[4]>'9') return 0;
d=(UART2_RX_buf[3]-48)*10+(UART2_RX_buf[4]-48); //分格式校验
if(d>=60) return 0;
if(UART2_RX_buf[6]<'0'||UART2_RX_buf[6]>'9'||UART2_RX_buf[7]<'0'||UART2_RX_buf[7]>'9') return 0;
s=(UART2_RX_buf[6]-48)*10+(UART2_RX_buf[7]-48); //秒格式校验
if(s>=60) return 0;
if(UART2_RX_buf[11]!='1'&&UART2_RX_buf[11]!='2') return 0; //通道格式校验
if(UART2_RX_buf[13]<'1'&&UART2_RX_buf[13]>'9') return 0; //持续时间格式校验
PWM_OUT_DATA[0]=h;PWM_OUT_DATA[1]=d;PWM_OUT_DATA[2]=s;
PWM_OUT_DATA[3]=UART2_RX_buf[11]; PWM_OUT_DATA[4]=UART2_RX_buf[13]-48;
//计算结果时间
PWM_OUT_DATA[4]+=PWM_OUT_DATA[2];
if(PWM_OUT_DATA[4]>=60) {PWM_OUT_DATA[4]-=60;} //时间超过60秒
return 1;
}
}
return 0;
}
void UART2_function(void)
{
if(UART2_over)
{
UART2_over=0;
if(UART2_DATA_check())
{
PWM_OUT_DATA[5]=1;
LCD_DisplayStringLine2(Line7,UART2_RX_buf,UART2_LEN,2);
}
HAL_UART_Receive_DMA(&huart2,UART2_RX_buf,50);
}
}
/* USER CODE END 1 */
LED功能函数
void LED_function(void)
{
static u8 LED_state_last=0XFF;
if(LED_state_last!=LED_state)
{
LED_state_last=LED_state;
LED_write(LED_state);
}
}
LCD功能函数
void LCD_function(void)
{
if(LCD_flag&(0x01<<2)) {LCD_flag&=(~(0x01<<2));LCD_DisplayStringLine(Line2,LCD_Line2_buf);}
if(LCD_flag&(0x01<<3)) {LCD_flag&=(~(0x01<<3));LCD_DisplayStringLine(Line3,LCD_Line3_buf);}
if(LCD_flag&(0x01<<4)) {LCD_flag&=(~(0x01<<4));LCD_DisplayStringLine(Line4,LCD_Line4_buf);}
if(LCD_flag&(0x01<<5)) {LCD_flag&=(~(0x01<<5));LCD_DisplayStringLine(Line5,LCD_Line5_buf);}
if(LCD_flag&(0x01<<7)) {LCD_flag&=(~(0x01<<7));LCD_DisplayStringLine(Line7,LCD_Line7_buf);}
#if PWM_IN_ON
if(LCD_flag&(0x01<<8)) {LCD_flag&=(~(0x01<<8));LCD_DisplayStringLine(Line8,LCD_Line8_buf);}
if(LCD_flag&(0x01<<9)) {LCD_flag&=(~(0x01<<9));LCD_DisplayStringLine(Line9,LCD_Line9_buf);}
#endif
}
宏定义功能说明
此处修改宏定义值
PWM_IN_ON 0 不使用TI2_CH4的输入捕获功能 1 使用TI2_CH4的输入捕获功能
PWM_IN_ON_TIM7 0 使用TIM2为输入捕获计时 1 使用TIM7为输入捕获计时
完整程序下载
下载链接:
链接:https://pan.baidu.com/s/1LMECSy3GuABduu42YUJdSQ
提取码:qy2o