开发工具:Altium Designer 2020、STM32CubeMX 5.3.0、MDK-ARM 5.28
1.设计需求
设计出一套完整的烟雾产生装置,该装置通过按钮来控制烟雾的产生和关闭。装置对体积要求较高,所以控制板需控制在4cm*3cm,同时根据装置所要安装的器件来灵活调整控制板的形状,具体功能需求如下:
(1)能连续产生烟雾;
(2)采用锂电池方式供电,锂电池充电具有正在充电和充电完成指示,电池电量低提醒;
(3)烟雾需呈现蓝色和红色两种颜色,颜色根据需要进行切换;
(4)加热电路电流、电压监测;
(5)采用无线方式控制烟雾效果。
2.设计方案
(1)设备电源
整个设备供电采用小型锂电池,预留有线电源供电接口,能保证整机持续工作约2小时;设计锂电池充电电路,通过Micro USB接口为锂电池充电并在充电进行和充电结束时LED提醒;设计LDO电源为系统控制器及其他电路供电。
(2)烟雾产生
利用加热丝将加热棉中的电子烟油进行加热,以便产生烟雾;为避免加热丝连续通电对使用寿命和烟雾效果造成影响,采用PWM控制电流加热,并将出烟量调节至最佳状态;设计电压与电流检测电路,检测加热过程电压值和电流值。
(3)烟雾效果
①烟雾喷出速度: 通过控制风机转速实现,并设定几个可选择挡位;
②烟雾颜色: 通过不同颜色的LED灯来进行控制,通过不同灯光的照射使烟雾呈现不同的颜色。
(4)出烟控制
出烟控制分为烟雾产生开关、风机开关、双色LED开关,开关的控制均通过STM32F030F4P6控制,并使用 LC12S无线模块控制烟雾大小、风扇转速、烟雾颜色。
(5)低功耗
发烟器3min未接收到控制指令,自动休眠无线模块并将STM32进入停止模式,收到控制命令时自动唤醒。
3.硬件电路
电路原理图:SMKController.SchDoc
PCB:SMKController.PcbDoc
4.软件代码
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdlib.h>
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
#define Close_LED 0x00
#define Open_RedLED 0x01
#define Open_BlueLED 0x02
#define FAN_Speed_0 0x00
#define FAN_Speed_1 0x01
#define FAN_Speed_2 0x02
#define FAN_Speed_3 0x03
/* USER CODE BEGIN PD */
void ADC_Check(void);
void OUTPUT_PWM(void);
void OUTPUT_PWM_first(void);
void OUTPUT_FAN(uint16_t FAN_MODE);
void LED_Control(void);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim);
void StopMode_Measure(void);
/* USER CODE END PD */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
uint16_t dutyCycle1=0; //占空比0%
uint16_t dutyCycle2=300; //占空比30%
uint16_t dutyCycle3=500;
uint16_t dutyCycle600=600;
uint16_t dutyCycle4=700;
uint16_t dutyCycle5=800;
uint16_t dutyCycle1000=1000;
uint8_t aTxBuffer1[1]; //串口接收命1缓存
uint8_t aTxBuffer2[1]; //串口接收命2缓存
uint8_t aTxBuffer3[1]; //串口接收命3缓存
uint8_t aRxBuffer[10]; //串口接收的命令
uint8_t i; //串口命令计数
uint8_t Sum=0; //串口命令校验和
uint32_t ADC_Value[100]; //ADC_DMA模式采集数据缓存
uint32_t Battery_Value_Check,Res_Current_Check; //电池电压、发热丝电流
uint16_t FAN_MODE; //风扇档位
uint8_t aTxCheck_PWM[1]; //PWM输出数据缓存
uint8_t aTxCheck_LED[1]; //LED灯光控制数据缓存
uint8_t TIM_2S_DONE; //2s计数完成标志 1:完成 0:未完
uint8_t TIM_12S_DONE=2; //12s计数完成标志 1:完成 0:未完
uint8_t TIM_3M_DONE; //3min计数完成标志 1:完成 0:未完
uint8_t flag = 2;
uint8_t Count_Flag = 0; //3min计数过程标志
uint8_t Wait_Entry_Sleep = 0; //3min计数
uint8_t uint8_t Stop_Mode_flag; //停止模式标志位 1:True 2:False
uint8_t BlueLED_Flag=0; //蓝光控制标志 1:开 0:关
uint8_t RedLED_Flag=0; //红光控制标志 1:开 0:关
uint8_t LED_Close_Flag=0; //灯光关闭标志
uint8_t Uart_RX_Date_Flag=0; //串口接收数据标志 1:接收到运行命令 0:接收到结束命令
uint8_t Uart1_PWM_Flag=0; //串口发送PWM输出命令标志位
uint8_t dmaflag=0; //DMA标志位
uint8_t Tim14BaseCnt=0; //7ms计数
uint8_t Tim14BaseCnt_PWM_first=0; //12s计数
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC_Init();
MX_TIM3_Init();
MX_TIM14_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc, (uint32_t*)ADC_Value, 100); //使能ADC_DMA采集模式
if(HAL_UART_Receive_IT(&huart1,aRxBuffer,10)!=HAL_OK)Error_Handler(); //开启串口中断接收模式
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); //使能PWM输出
HAL_TIM_Base_Start_IT(&htim14); //使能TIM计时
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
ADC_Check(); //ADC采集电池电压及发热丝电流
OUTPUT_PWM_first(); //PWM输出
LED_Control(); //双色LED灯光控制
OUTPUT_FAN(FAN_MODE); //风扇转速控制
if(Stop_Mode_flag == 1) //停止模式标志位判断
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_SET); //休眠串口无线模块
Stop_Mode_flag = 2;
StopMode_Measure(); //进入停止模式
}
/* USER CODE END WHILE */
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* ADC RxdCallback Fuction */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
dmaflag = 1;
HAL_ADC_Stop_DMA(hadc);
}
/* Base Timer Callback Fuction */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim) //每1ms进入一次中断,1ms计数一次
{
if(htim->Instance == htim14.Instance)
{
/* PWM_First 12s计时 */
if (TIM_12S_DONE == 0)
{
if(Tim14BaseCnt_PWM_first<12000)
{
Tim14BaseCnt_PWM_first++;
}
else
{
TIM_12S_DONE = 1;
flag = 1;
Tim14BaseCnt_PWM_first=0;
}
}
/* 12s后PWM间隔输出2s计时 */
if (TIM_12S_DONE == 1)
{
if(Tim14BaseCnt<70)
{
Tim14BaseCnt++;
}
else
{
Tim14BaseCnt= 0;
TIM_2S_DONE = abs(TIM_2S_DONE-1);
}
}
/* 接收结束命令后3min计时进入停止模式,3min内有非结束命令时自动退出计时 */
if (TIM_3M_DONE == 1)
{
if(Wait_Entry_Sleep < 180000)
{
if(Uart_RX_Date_Flag==0)
{
Wait_Entry_Sleep++;
Count_Flag++;
}
else if(Uart_RX_Date_Flag==1)
{
Count_Flag=200000;
Wait_Entry_Sleep=180000;
}
}
if(Count_Flag==200000)
{
Wait_Entry_Sleep = 0;
TIM_3M_DONE = 2;
Count_Flag=0;
}
else if(Count_Flag>179998)
{
Stop_Mode_flag = 1;
Wait_Entry_Sleep = 0;
TIM_3M_DONE = 2;
Count_Flag=0;
}
}
}
}
/* Usart RxdCallback Fuction */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
for(i=2;i<=8;i++)
{
Sum = Sum + aRxBuffer[i];
}
if(Sum%302==aRxBuffer[9] && aRxBuffer[2]==0x0A) //校验和
{
Uart_RX_Date_Flag=1;
/* PWM串口命令分析 */
if(aRxBuffer[3]==0 && aRxBuffer[4]==0 && aRxBuffer[5]==0) //当接收到结束命令时,开启3min计时
{
TIM_3M_DONE=1;
Uart_RX_Date_Flag=0;
}
aTxBuffer1[0]=aRxBuffer[3];
if(aTxCheck_PWM[0]==aTxBuffer1[0]) //防止第二次相同命令重新运行
{
aTxCheck_PWM[0]=aTxBuffer1[0];
}
else if(aTxBuffer1[0]==0x00) //对第一个PWM输出命令进行判断
{
Uart1_PWM_Flag = 0;
flag=0;
TIM_12S_DONE = 2;
aTxCheck_PWM[0]=aTxBuffer1[0];
}
else if(aTxBuffer1[0]==0x01)
{
Uart1_PWM_Flag = 1;
TIM_12S_DONE = 0;
flag=0;
TIM_2S_DONE = 0;
aTxCheck_PWM[0]=aTxBuffer1[0];
}
/* LED串口命令分析 */
aTxBuffer2[0]=aRxBuffer[4];
switch(aTxBuffer2[0]) //对第二个LED灯光控制命令进行判断
{
case Open_BlueLED:
BlueLED_Flag=1;
break;
case Open_RedLED:
RedLED_Flag=1;
break;
case Close_LED:
LED_Close_Flag=1;
break;
default:;
}
/* FAN串口命令分析 */
aTxBuffer3[0]=aRxBuffer[5];
switch(aTxBuffer3[0])
{
case FAN_Speed_0:
FAN_MODE = 0;
break;
case FAN_Speed_1:
FAN_MODE = 1;
break;
case FAN_Speed_2:
FAN_MODE = 2;
break;
case FAN_Speed_3:
FAN_MODE = 3;
break;
default:;
}
Sum=0;
}
else
{
Sum=0;
memset(aRxBuffer, 0, sizeof aRxBuffer); //命令错误清除缓冲区域数据
}
HAL_UART_Receive_IT(&huart1,aRxBuffer,10); //再次开启串口接收
}
}
/**
* ADC_DMA模式采集函数
* 判断电池电压及发热丝电流,用led灯显示
*/
void ADC_Check(void)
{
if(dmaflag == 1) //判断中断标志,调用ADC Check函数。
{
dmaflag = 0;
for(i=0,Battery_Value_Check=0,Res_Current_Check=0; i<100;i++)
{
Battery_Value_Check = ADC_Value[i]*3000/4096*2;
Res_Current_Check = ADC_Value[i+1]*3000/4069/0.05/100;
if(Battery_Value_Check>0 && Res_Current_Check>0)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_2);
}
HAL_ADC_Start_DMA(&hadc, (uint32_t*)ADC_Value, 100); //重新使能ADC_DMA采集模式
}
}
}
/**
* 电阻丝加热控制,加热开始阶段
* 前12s阶段PWM输出控制函数
* PWM_first输出控制,前12s输出占空比为80%的PWM波
*/
void OUTPUT_PWM_first(void)
{
if (Uart1_PWM_Flag == 1)
{
if(flag == 1)
{
OUTPUT_PWM();
}
if(flag == 0) //前12s阶段PWM输出控制函数
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,dutyCycle5); //输出占空比为80%的PWM波
}
}
if(Uart1_PWM_Flag == 0)
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
TIM_2S_DONE = 0;
}
}
/**
* 12秒后阶段PWM输出控制函数
* PWM输出控制,12s后输出80%占空比的PWM波
*/
void OUTPUT_PWM(void)
{
if(TIM_2S_DONE == 1)
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,dutyCycle5);
}
if(TIM_2S_DONE == 0)
{
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);
}
}
/**
* LED灯光控制函数
* 分别对红蓝光进行开关控制
*/
void LED_Control(void)
{
if(BlueLED_Flag==1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
BlueLED_Flag=0;
}
if(RedLED_Flag==1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
RedLED_Flag=0;
}
if(LED_Close_Flag==1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
LED_Close_Flag=0;
}
}
/**
* 风扇转速控制函数
* PWM输出控制,进行各个风扇档位进行控制,默认档位为占空比60%
*/
void OUTPUT_FAN(uint16_t FAN_MODE)
{
switch(FAN_MODE)
{
case 0:
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutyCycle1);
break;
case 1:
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutyCycle2);
break;
case 2:
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutyCycle3);
break;
case 3:
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutyCycle4);
break;
default:
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,dutyCycle600);
}
}
/**
* 停止模式函数
* 进入停止模式,等待EXTI唤醒
*/
void StopMode_Measure(void)
{
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_Delay(200);
}
/**
* EXTI_Callback函数
* 按键中断将单片机从停止模式唤醒
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == SW_Pin)
{
SystemClock_Config(); //重新使能时钟,因为停止模式将其关闭
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_RESET); //唤醒串口无线模块
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2); //按键提示灯,可删除
__HAL_GPIO_EXTI_CLEAR_IT(SW_Pin); //Clear the EXTI's line pending bits.
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
本次实践的烟雾发生器是三年前导师项目中所需的一个小装置,软硬件基础功能都已实现,这算是自己做的第一个嵌入式实践,硬件和软件设计可能还不规范,在此做一个整理记录。在烟雾效果的调试过程中很有趣,需将加热电流大小、加热时间、风力大小、加热丝粗细以及加热棉材料等因素进行合理的选择,才能达到烟雾的最佳效果,否则会导致烟雾效果不佳和加热丝熔断的情况发生。