4T测评上的一道题,关于使用电压当前值,max,min和串口控制、温度采集控制PWM的一道题

本文详细描述了在STM32平台上,如何利用ADC、DMA进行电压采集,通过滤波处理和趋势分析,并在LCD上实时显示电压值、最大值、最小值和趋势,同时实现串口通信与DS18B20温度监控以及PWM控制功能。
摘要由CSDN通过智能技术生成

题解

电压采集与显示

 按照题干要求我们每100ms使用任意一个ADC采集电压,但要记录实时电压与最大和最小电压,并显示电压趋势。

100ms就用定时器来处理吧,每100ms进入一次中断

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM1)//每100ms采样一次
	{
		ADC2_Value();
		
	}
}

 ADC如何配置就不讲了,前面文章有。

下面讲ADC2_Value()

void ADC2_Value()
{
	HAL_ADC_Start_DMA(&hadc2,(uint32_t *)adc_buffer,10);

	uint32_t value=0;
	for(int i=0;i<10;i++)
	{
		value=value+adc_buffer[i];
	}
	
	if(first<2)
	{
		v_cur=3.3*(value/10.0)/(1<<12);
		v_max=v_cur;
		v_min=v_max;
		v_last=v_cur;
		first++;
		trend=1;
	}else{
		v_cur=3.3*(value/10.0)/(1<<12);
	}
	
}

 首先我开启了ADC的DMA传输,然后使用均值滤波来处理数据,因为在采集的时候可能有采集值为0的情况,这样使用滤波后数据就更精确了,然后我设置了一个全局first用来控制进入初始数据设置代码段的次数,为什么设置进入两次?因为我在实际测试时发现进入一次是采集不到的,数据全是0,这样会影响最低电压的设置(因为我更新最大最小电压是通过比较的方式,一旦最低设置为0 ,就无法准确显示最低电压了,只能是0了)。我将每个数据(当前值最大最小值)都初始化成第一次采集到的当前电压值,(这也符合逻辑,没有电压变化,这三个值就是一样的)。全局变量trend是用来确定变化趋势的变量,初始设置为增加方向(>>)。

之后再进行数据采集就只是在这里更新当前电压。

然后是电压大小比较函数:

当前电压大于最大电压记录就更新,最小电压更新也是一样的道理。

void value_max_min()
{
	v_max=v_cur>v_max ? v_cur:v_max;
	v_min=v_cur<v_min ? v_cur:v_min;
}

下面是趋势判断函数:

//1-up,2-down,3-keep
void v_trend()
{
	if((uint8_t)v_cur>(uint8_t)v_last)
	{
		v_last=v_cur;
		trend=1;
	}else if((uint8_t)v_cur<(uint8_t)v_last)
	{
		v_last=v_cur;
		trend=2;
	}else{
		v_last=v_cur;
		trend=trend;
	}
	
}

 获得的trend值会在LCD现实函数里控制显示哪一个趋势符号。

主函数的while函数:

对了这份代码我一起把DHT11的代码也加进去了。。。

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  value_max_min();
		v_trend();
//	  DHT11_GetValue();
		 LCD_Display();
		PWM_control();
  }
  /* USER CODE END 3 */
}

 LCD显示

void LCD_Display()
{
	
	memset((void *)LCD_String,0,sizeof(LCD_String));
	sprintf((char*)LCD_String,"        DATA");
	LCD_DisplayStringLine(Line1,LCD_String);
	
	memset((void *)LCD_String,0,sizeof(LCD_String));
	sprintf((char*)LCD_String,"      V:%1.1f",v_cur);
	LCD_DisplayStringLine(Line3,LCD_String);
	
	memset((void *)LCD_String,0,sizeof(LCD_String));
	sprintf((char*)LCD_String,"      Vmax:%1.1f",v_max);
	LCD_DisplayStringLine(Line4,LCD_String);
	
	memset((void *)LCD_String,0,sizeof(LCD_String));
	sprintf((char*)LCD_String,"      Vmin:%1.1f",v_min);
	LCD_DisplayStringLine(Line5,LCD_String);
	
	switch(trend)
	{
		case 1:
		    memset((void *)LCD_String,0,sizeof(LCD_String));
	        sprintf((char*)LCD_String,"      Vx:>>");
	        LCD_DisplayStringLine(Line6,LCD_String);
		     break;
		case 2:
			 memset((void *)LCD_String,0,sizeof(LCD_String));
	        sprintf((char*)LCD_String,"      Vx:<<");
	        LCD_DisplayStringLine(Line6,LCD_String);
		
		     break;
		default:break;
	};
	memset((void *)LCD_String,0,sizeof(LCD_String));
	sprintf((char*)LCD_String,"      T:%2.1f",ds18b20_read()/16.0);
	LCD_DisplayStringLine(Line7,LCD_String);
	
	
//	memset((void *)LCD_String,0,sizeof(LCD_String));
//	sprintf((char*)LCD_String,"      DH-H:%2.1f",DH_Humi);
//	LCD_DisplayStringLine(Line8,LCD_String);
//	
//	memset((void *)LCD_String,0,sizeof(LCD_String));
//	sprintf((char*)LCD_String,"      DH-T:%2.1f",DH_Temp);
//	LCD_DisplayStringLine(Line9,LCD_String);
}

 串口控制

 串口工能我使用了DMA加空闲中断的方式,前文我也写过(STM32的串口收发补充,使用DMA+空闲中断

main函数while之前:

 HAL_UARTEx_ReceiveToIdle_DMA(&huart1,USART1_RX,20);

 中断函数里:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,USART1_RX,20);
  /* USER CODE END USART1_IRQn 1 */
}

 使用空闲中断回调处理:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart->Instance==USART1)
	{
		if (strcmp((char *)USART1_RX, "AT\r\n") == 0) {
                HAL_UART_Transmit(&huart1,(uint8_t *)"AT\r\n",sizeof("AT\r\n"),10);
            } else if (strncmp((char*)USART1_RX, "AT+V", 4) == 0 && strlen((char*)USART1_RX) == 9 && USART1_RX[5] >= '0' && USART1_RX[5] <= '2' && USART1_RX[7] == '\r') {
                unsigned char response[20]={0};
			float temp;
			temp=(USART1_RX[5]=='1') ? v_max:((USART1_RX[5]=='2') ? v_min:v_cur);
                sprintf((char*)response, "+V[%c]: %1.2f\r\n",USART1_RX[5],temp );
                HAL_UART_Transmit(&huart1,(uint8_t *)response,sizeof(response),10);
            }
			memset(USART1_RX,0,sizeof(USART1_RX));
	}
}

 分段解释代码:

按题干要求我们收到:"AT\r\n"就回复"AT\r\n":

if (strcmp((char *)USART1_RX, "AT\r\n") == 0) {
                HAL_UART_Transmit(&huart1,(uint8_t *)"AT\r\n",sizeof("AT\r\n"),10);
            } 

 使用strcmp函数比较字符串是否相同。

按题干要求我们收到:"AT+V[0..2]\r\n"就回复"+V[0..2]:DATE\r\n":

比如收到"AT+V[0]\r\n"就要回复"+V[0]:DATE\r\n",DATE是数据‘[ ]’不要丢,

else if (strncmp((char*)USART1_RX, "AT+V", 4) == 0 && strlen((char*)USART1_RX) == 9 && USART1_RX[5] >= '0' && USART1_RX[5] <= '2' && USART1_RX[7] == '\r') {
                unsigned char response[20]={0};
			float temp;
			temp=(USART1_RX[5]=='1') ? v_max:((USART1_RX[5]=='2') ? v_min:v_cur);
                sprintf((char*)response, "+V[%c]: %1.2f\r\n",USART1_RX[5],temp );
                HAL_UART_Transmit(&huart1,(uint8_t *)response,sizeof(response),10);
            }

 第二个指令判断比第一个略微复杂一点:首先检测"AT+V"这个是与上一个指令的区别点,然后我们再检测后面的数据帧对不对,比如长度和[ ]中的取值是否在范围,都符合进入下面的条件判断,赋给相应的值:

temp=(USART1_RX[5]=='1') ? v_max:((USART1_RX[5]=='2') ? v_min:v_cur);

 然后组成新的帧发送:

sprintf((char*)response, "+V[%c]: %1.2f\r\n",USART1_RX[5],temp ); 

HAL_UART_Transmit(&huart1,(uint8_t *)response,sizeof(response),10);

最后不要忘了清空DMA缓存为下一次接收数据做准备:

memset(USART1_RX,0,sizeof(USART1_RX));

最后这是测评的图: 

温度采集与PWM控制

 温度如何采集我在这篇文章里写了:蓝桥杯——扩展板DS18B20和DHT11

PWM的配置与使用:蓝桥杯——定时器篇(2)

那么接下来就是简单的逻辑控制:

volatile double temp;
void PWM_control()
{
	
	temp=ds18b20_read()/16.0;
	if(temp<25.0)
	{
		 __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,100);
	}else if(temp>=25.0&&temp<30.0)
	{
		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,400);
	}else if(temp>=30.0)
	{
		__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,800);
	}
}

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

满城烟雨DLRY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值