第五届 蓝桥杯 嵌入式设计与开发项目 省赛

本文详细介绍了基于STM32的双路输出控制器设计,包括按键功能、串口通讯、RTC时间设定、LCD显示和LED指示等功能。系统通过串口接收命令设定输出时间和占空比,使用独立按键调整通道PA1和PA2的输出。同时,通过EEPROM存储参数,并解决了串口2与PWM的冲突问题,确保了系统的稳定运行。
摘要由CSDN通过智能技术生成

赛题

一、题目
“双路输出控制器”具有信号输出时间设定、输出信号占空比调整、当前输出通道及时间显示、系统工作参数存储、串口通讯及 LED 指示等功能。
“双路输出控制器”通过串口完成信号输出时间设定功能;通过 EEPROM 完成系统工作参数存储功能;通过按键完成输出通道切换、输出信号占空比调整及停止信号输出功能;
系统硬件电路主要由 MCU 控制单元、独立按键、LCD 显示单元、串口通讯单元、EEPROM 数据存储单元和 LED 指示单元组成,系统框图如图 1 所示:

设计任务及要求

  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 中。

  1. 串口通讯单元

     系统可通过串口接收命令,用户输入字符串“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 秒后输出低电平信号。
    
  2. LCD 显示单元

    通过 LCD 显示当前 EEPROM 中存储的脉宽调制信号占空比、系统时间、当前正在输出的通道以及通过串口接收到的命令,LCD 显示界面参考示意图如图 2、图 3 所示:
    
  3. EEPROM 数据存储单元

     通过 EEPROM 存储 PA1、PA2 输出信号的占空比,占空比数值可以通过按键调整。
    
  4. LED 指示功能
    通道 PA1 输出脉宽调制信号时,指示灯 LD1 点亮,其余指示灯处于熄灭状态;
    通道 PA2 输出脉宽调制信号时,指示灯 LD2 点亮,其余指示灯处于熄灭状态。

  5. 系统工作及初始化状态说明

     系统初始化时间设定为 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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值