3734|6

58

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【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); //启动ADC转换
  • TIM_Cmd(TIM2,ENABLE); //使能定时器
  • while(1)
  • {
  • if(exti0_status == 1) //光敏电阻感到光
  • {
  • OLED_DisPlay_On(); //开启OLED
  • }else
  • {
  • OLED_DisPlay_Off(); //关闭oled
  • }
  • if(refre != 0) //500ms刷屏一次
  • {
  • if(count > 120 ){ //mq初次上电有预热时间
  • OLED_Showdecimal(0,38,MQ7_GetPPM(),4,1,24);
  • OLED_ShowString(92,46,"PPM",16);//PPM
  • MQ_ready = 1;
  • if(ADC_GetFlagStatus(ADC1, ADC_IT_AWD)!= SET){
  • ADC_ITConfig( ADC1, ADC_IT_AWD, ENABLE);
  • }
  • }
  • if(DHT11_Updata > 2) //1.5秒读取一次
  • { /*调用DHT11_Read_TempAndHumidity读取温湿度,若成功则输出该信息*/
  • 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);//by
  • OLED_ShowChinese(64,48,7,16);//梨
  • OLED_ShowChinese(80,48,8,16);//子
  • OLED_Refresh();
  • Delay_Ms(3000);
  • }
  • //传感器主界面
  • void Main_page(void)
  • {
  • //DHT11数据
  • 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);//C
  • 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);//RH
  • 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~~~~

GPIO_Toggle.7z

393.38 KB, 下载次数: 3

最新回复

风扇是怎么驱动的?还有蜂鸣器是有源的还是无源的?   详情 回复 发表于 2021-4-20 11:04
点赞 关注
 
 

回复
举报

2万

帖子

74

TA的资源

管理员

沙发
 

生活中的智慧,考虑的情景挺周全的。

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复

1万

帖子

202

TA的资源

管理员

板凳
 

赞!视频可以通过编辑器上的视频按钮,直接把B站的视频链接贴上去就可以在线播放了

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名玩板看这里:
https://bbs.eeworld.com.cn/elecplay.html
EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!
 
 
 

回复

1万

帖子

202

TA的资源

管理员

4
 

也可以把视频上传到我们的大学堂

加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名玩板看这里:
https://bbs.eeworld.com.cn/elecplay.html
EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!
 
 
 

回复

7763

帖子

2

TA的资源

五彩晶圆(高级)

5
 

很详细,谢谢分析!

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

9856

帖子

24

TA的资源

版主

6
 

风扇是怎么驱动的?还有蜂鸣器是有源的还是无源的?

点评

GPIO驱动三极管或场管开关来控制风扇和蜂鸣器的.蜂鸣器无所谓够响亮就行,有源就直接给电断电,无源就PWM就ok了  详情 回复 发表于 2021-4-20 22:23
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

58

帖子

0

TA的资源

一粒金砂(中级)

7
 
littleshrimp 发表于 2021-4-20 11:04 风扇是怎么驱动的?还有蜂鸣器是有源的还是无源的?

GPIO驱动三极管或场管开关来控制风扇和蜂鸣器的.蜂鸣器无所谓够响亮就行,有源就直接给电断电,无源就PWM就ok了

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
有奖直播报名| TI 面向楼宇和工厂自动化行业的毫米波雷达解决方案
【内容简介】TI 60GHz IWRL6432和 IWRL1432毫米波雷达传感器如何帮助解决楼宇和工厂自动化应用中的感应难题
【直播时间】5月28日(周三)上午10:00
【直播礼品】小米双肩包、contigo水杯、胶囊伞、安克充电器

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 1

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表