【RISC-V MCU CH32V103测评】+ 一氧化碳报警器
[复制链接]
一氧化碳是无色、无味、无刺激性的有毒的气体.含碳物质不完全燃烧时均会产生一氧化碳,例如部分家庭燃煤取暖,燃气热水器,机车尾气等.吸入高浓度一氧化碳可致人死亡,一氧化碳会影响氧和红细胞结合.
50PPM已经是安全极限了,200PPM会造成轻微头痛,400PPM则可引起呕吐甚至眩晕,浓度再高就可能致人死亡.因其无色无味,犹如无形杀手,每年因一氧化碳中毒而死亡的也不再少数,年前我一位亲人也因一氧化碳中毒而去世.
目前市面上的一氧化碳报警器仅有声光报警,不能主动通风,降低浓度,挽救生命.
本设计基于沁恒RISC-V架构的CH32V103, 在达到额定浓度声音报警之外还将控制排风扇进行强制通风,由CH32V103采集一氧化碳传感器的模拟电压并转换成有效浓度值,采集DHT11的温湿度数据,一并输出给OLED显示,两路GPIO控制蜂鸣器和排风扇.为了更人性化,光敏电阻模块通过感知光下线,开关OLED,避免晚上成小夜灯(我晚上有光就睡不着).其ADC的模拟看门狗实时检测CO传感器电压,超过设定值便响蜂鸣器报警浓度超过监控值,排风扇进行强制通风,降低CO浓度.OLED显示CO浓度.以达到警示保护生命安全.
以下为工程代码:
- Opening_page();
- OLED_Clear();
- if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) != Bit_SET)
- {
- exti0_status = 1;
- }
-
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);
- TIM_Cmd(TIM2,ENABLE);
- while(1)
- {
-
-
- if(exti0_status == 1)
- {
- OLED_DisPlay_On();
-
- }else
- {
- OLED_DisPlay_Off();
-
- }
- if(refre != 0)
- {
- if(count > 120 ){
- OLED_Showdecimal(0,38,MQ7_GetPPM(),4,1,24);
- OLED_ShowString(92,46,"PPM",16);
- MQ_ready = 1;
- if(ADC_GetFlagStatus(ADC1, ADC_IT_AWD)!= SET){
- ADC_ITConfig( ADC1, ADC_IT_AWD, ENABLE);
- }
- }
- if(DHT11_Updata > 2)
- {
- DHT11_Read_TempAndHumidity ( & DHT11_Data ) ? Main_page() : DHT_ERR();
- DHT11_Updata = 0;
- }
-
- }
- refre = 0;
-
- }
- }
-
-
-
- void Opening_page(void)
- {
- OLED_Clear();
- OLED_ShowChinese(8,16,0,16);
- OLED_ShowChinese(24,16,1,16);
- OLED_ShowChinese(40,16,2,16);
- OLED_ShowChinese(56,16,3,16);
- OLED_ShowChinese(72,16,4,16);
- OLED_ShowChinese(88,16,5,16);
- OLED_ShowChinese(104,16,6,16);
- OLED_ShowString(48,48,"by",16);
- OLED_ShowChinese(64,48,7,16);
- OLED_ShowChinese(80,48,8,16);
- OLED_Refresh();
- Delay_Ms(3000);
- }
-
-
- void Main_page(void)
- {
-
-
- OLED_ShowChinese(0,4,9,16);
- OLED_ShowChinese(16,4,10,16);
- OLED_ShowChar(32,4,':',16);
- OLED_ShowNum(40,4,DHT11_Data.temp_int,2,16);
- OLED_ShowChar(60,4,'.',16);
- OLED_ShowNum(68,4,DHT11_Data.temp_deci,1,16);
- OLED_ShowChar(90,4,'C',16);
-
- OLED_ShowChinese(0,20,11,16);
- OLED_ShowChinese(16,20,10,16);
- OLED_ShowChar(32,20,':',16);
- OLED_ShowNum(40,20, DHT11_Data.humi_int,2,16);
- OLED_ShowChar(60,20,'.',16);
- OLED_ShowNum(68,20, DHT11_Data.humi_deci,1,16);
- OLED_ShowChar(90,20,'%',16);
- OLED_ShowString(100,20,"RH",16);
- OLED_Refresh();
- }
-
- void DHT_ERR(void)
- {
-
- OLED_Clear();
- OLED_ShowChinese(0,4,9,16);
- OLED_ShowChinese(16,4,11,16);
- OLED_ShowChinese(32,4,10,16);
- OLED_ShowChinese(48,4,12,16);
- OLED_ShowChinese(64,4,13,16);
- OLED_ShowChinese(80,4,14,16);
- OLED_ShowChinese(96,4,15,16);
- OLED_ShowChar(112,4,'!',16);
- OLED_Refresh();
- }
-
中断服务函数
- void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- void ADC1_2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- extern uint8_t exti0_status;
- extern uint8_t DHT11_Updata;
- extern uint8_t boot_time_ms;
- extern uint8_t refre;
- extern __IO uint16_t ADC_Value;
- extern uint8_t count;
- extern uint8_t MQ_ready;
- /*******************************************************************************
- * Function Name : NMI_Handler
- * Description : This function handles NMI exception.
- * Input : None
- * Return : None
- *******************************************************************************/
- void NMI_Handler(void)
- {
- }
-
- /*******************************************************************************
- * Function Name : HardFault_Handler
- * Description : This function handles Hard Fault exception.
- * Input : None
- * Return : None
- *******************************************************************************/
- void HardFault_Handler(void)
- {
- while (1)
- {
- }
- }
-
- /*******************************************************************************
- * Function Name : EXTI0_IRQHandler
- * Description : This function handles EXTI0 Handler.
- * Input : None
- * Return : None
- *******************************************************************************/
- void EXTI0_IRQHandler(void)
- {
- if(EXTI_GetITStatus(EXTI_Line0)!=RESET)
- {
- if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) != Bit_SET)
- {
- exti0_status = 1;
- }else
- {
- exti0_status = 0;
- }
- EXTI_ClearITPendingBit(EXTI_Line0); /* Clear Flag */
- }
- if(EXTI_GetITStatus(EXTI_Line4)!=RESET)
- {
-
- if( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4)!= Bit_SET)
- {
-
- GPIO_ResetBits(GPIOA,GPIO_Pin_3);
- }
-
- EXTI_ClearITPendingBit(EXTI_Line4);
- }
- }
-
- /*******************************************************************************
- * Function Name : TIM2_IRQHandler
- * Description : This function handles TIM2 Handler.
- * Input : None
- * Return : None
- *******************************************************************************/
- void TIM2_IRQHandler(void)
- {
- if(TIM_GetFlagStatus(TIM2, TIM_IT_Update)!= RESET){
- refre = 1;
- DHT11_Updata ++;
- boot_time_ms ++;
- if(MQ_ready == 0){
- count++;
- }
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);
- }
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
- }
-
- /*******************************************************************************
- * Function Name : ADC1_2_IRQHandler
- * Description : This function handles analog wathdog exception.
- * Input : None
- * Return : None
- *******************************************************************************/
- void ADC1_2_IRQHandler(void)
- {
- if(ADC_GetITStatus( ADC1, ADC_IT_AWD)){
- if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2 | GPIO_Pin_3)!= Bit_SET){
- GPIO_SetBits(GPIOA,GPIO_Pin_2 | GPIO_Pin_3);
- }
- }
- if(ADC_Value<1500){
- GPIO_ResetBits(GPIOA,GPIO_Pin_2 | GPIO_Pin_3);
- }
-
- ADC_ClearITPendingBit( ADC1, ADC_IT_AWD);
- }
-
-
ADC及DMA配置
-
- /*******************************************************************************
- * Function Name : ADC_Function_Init
- * Description : Initializes ADC collection.
- * Input : None
- * Return : None
- *******************************************************************************/
- void ADC_Function_Init(void)
- {
- ADC_InitTypeDef ADC_InitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
- RCC_ADCCLKConfig(RCC_PCLK2_Div8);
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- ADC_DeInit(ADC1);
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
- ADC_InitStructure.ADC_NbrOfChannel = 1;
- ADC_Init(ADC1, &ADC_InitStructure);
-
- ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );
-
- /* 高阈值:1400, 低阈值:0 */
- ADC_AnalogWatchdogThresholdsConfig(ADC1, 1300, 0);
- ADC_AnalogWatchdogSingleChannelConfig( ADC1, ADC_Channel_1);
- ADC_AnalogWatchdogCmd( ADC1, ADC_AnalogWatchdog_SingleRegEnable);
-
- NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
-
-
-
- ADC_Cmd(ADC1, ENABLE);
- ADC_DMACmd(ADC1, ENABLE); // ADC DMA 使能
-
- ADC_ResetCalibration(ADC1);
- while(ADC_GetResetCalibrationStatus(ADC1));
- ADC_StartCalibration(ADC1);
- while(ADC_GetCalibrationStatus(ADC1));
-
-
- }
-
- /*******************************************************************************
- * Function Name : DMA_Tx_Init
- * Description : Initializes the DMAy Channelx configuration.
- * Input : DMA_CHx:
- * x can be 1 to 7.
- * ppadr: Peripheral base address.
- * memadr: Memory base address.
- * bufsize: DMA channel buffer size.
- * Return : None
- *******************************************************************************/
-
- void DMA_Tx_Init(void)
- {
- DMA_InitTypeDef DMA_InitStructure;
-
- RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
-
- DMA_DeInit(DMA1_Channel1);
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->RDATAR;
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Value;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = 1;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
- DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA1_Channel1, &DMA_InitStructure );
- DMA_Cmd(DMA1_Channel1, ENABLE);
-
-
- }
-
-
SPI初始化
- void SPI1_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- SPI_InitTypeDef SPI_InitStructure;
-
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
-
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
-
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx ;
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
- SPI_InitStructure.SPI_CRCPolynomial = 7;
- SPI_Init( SPI1, &SPI_InitStructure );
-
- SPI_Cmd( SPI1, ENABLE );
- }
-
-
定时器时基配置
- void TIM2_Base_Init(void)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE );
-
- TIM_TimeBaseInitStructure.TIM_Period = 5000-1;
- TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
-
- TIM_ITConfig(TIM2,TIM_IT_Update, ENABLE);
-
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn ;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
-
- }
-
一氧化碳转换浓度
-
- /****************************************
- * RS/R0 ppm *
- * 1.6 50 *
- * 1 100 *
- * 0.6 200 *
- * 0.46 300 *
- * 0.39 400 *
- * 0.28 600 *
- * 0.21 1000 *
- * ppm = 98.322f * pow(RS/R0, -1.458f) *
- ****************************************/
- // 传感器校准函数
- void MQ7_PPM_Calibration(float RS)
- {
- R0 = RS / pow(CAL_PPM / 98.322, 1 / -1.458f);
- }
-
- // 获取传感器的值
-
- float MQ7_GetPPM(void)
- {
- float Vrl = 3.3f * ADC_Value / 4095.f;
- float RS = (3.3f - Vrl) / Vrl * RL;
- if(boot_time_ms > 6) // 获取系统执行时间,3s校准
- {
- MQ7_PPM_Calibration(RS);
- boot_time_ms =0;
- }
- float ppm = 98.322f * pow(RS/R0, -1.458f);
- return ppm;
- }
-
以下是功能演示:
搭建的硬件平台
上电开机
关灯效果,OLED关闭
开灯效果OLED打开,CO传感器有预热时间,时间没到,还没显示.
正常显示
达到报警阈值,风扇运行
回落到正常阈值,风扇停转
再次达到报警阀值,
以下是视频展示CO报警器功能演示
附上工程代码:
GPIO_Toggle.7z
总的来说CH32V103是替代STM32F103的产品,在引脚和驱动库上和STD库保持高度一致,且是基于RISC-V,免费开源不易被邦交政策影响,还有一个M3核的CH32F103的姊妹作伴,沁恒做了优化,可以无缝互相替代,
CH32V103用的PFIC中断控制器,
PFIC 控制器
44+3个可单独屏蔽中断,每个中断请求都有独立的触发和屏蔽位、状态位
提供一个不可屏蔽中断NMI
2级嵌套中断进入和退出硬件自动压栈和恢复,无需指令开销
4路可编程快速中断通道,自定义中断向量地址
希望官方可以出一个更详细介绍PDIC的文档以及例程参考.
Bye~~~~
|