网上买的JW01模块,二十六块,买贵了。看到最便宜有九块钱的(运费十块),心疼。。。避雷了兄弟们,看到二十六块的JW01别买。
这个模块它是1秒钟检测一次CO2然后通过串口来传输结果,并且还自带了数据检测,感觉还是不错的,这里记录一下。
商家提供的资料我看了,演示视频不能说是保教包会吧也算得上是聊胜于无了。
唯一有用的就是芯片手册了,不过芯片手册这个文件夹里也就几张图片。
还好JW01本身就简单,通过这几张图片我们也能大概知道是怎么使用。
可以知道JW01是通过UART来传输数据的,波特率9600,数据位8位,停止位1位,无校验,就是最普通的设置。
从上图可以知道JW01发送的串口数据流格式,一共是6个字节。
第一个字节是固定的2C,我们后面通过这个来使得获取的数据位对齐。
第二和第三个字节可以通过公式来得到检测到的CO2浓度。
然后第四和第五个字节好像也是固定的,是0x03和0xFF。
最后一个字节是校验位,也就是前五个字节加起来(要转为8位的数据格式,例如uint8_t)等于第六个字节的时候,我们得到的数据才是正确的。
还有一点要注意的就是JW01的输出口(TXD)是5V的(这个和供电的电压有关,如果给3.3V供电的话那么就不需要考虑这些了),而我们的单片机大概率是3.3V,因此要电平转换。
不过很幸运,我们STM32F103的USART1的RXD是可以接收5V电压的,因此不需要转换。
如上图所示,IO口有标注FT的引脚即是可以接受5V电压的。但是我们STM32F103的USART2就不能接受5V电压的,因此我们就使用USART1这样比较省事。
所以我们只需要把对应的口接到STM32F103的引脚上就行。
因为JW01需要5V供电,因此我将ST-Link的5V接出来供5V电了,经我实测,STM32F103的3.3V似乎也可以带得动JW01,不过预热速度会比5V稍慢一些。
并且我们只需要接收JW01的信息而不需要给它发送数据,因此我们甚至可以不配置STM32F103的TXD,只需要配置RXD即可,不过代码里我还是配置上了(因为用的是之前配置USART的代码)。
接下来的讲解配合我下面的代码食用。
我们首先先正常配置USART,可以参考我之前的文章。
由于我们是需要接收数据,因此需要打开接收数据的中断,中断优先级随便配一个就行。
因为JW01固定发送的是6字节的数据,因此我们使用容量为6个uint8_t的数组来存放数据。
在中断函数里,我们按照接收的顺序去把收到的数据放入数组里,但是要多一个判断,只有当我们准备存放在数值的第一位并且此时接收的数据为0x2C的时候(参考前文的数据包格式),我们才真正接收此数据,否则我们就一直卡在准备接受第一位数据的状态。
JW01的发送速率是一秒发一次。也就是说正常情况下我们数组里的数据是一秒更新一次。
接收之后我们显示检测的数据,CO2的浓度为数组第二个元素乘上256再加上数值第三个元素,单位为ppm。
并且之前也说了,六个字节的最后一个字节是校验位,前五个字节加起来要等于第六个字节,需要注意的是在比较的时候需要将比较结果转换为8个bit的数据,这样才能正确比较。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
void JW01_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能外设时钟
GPIO_InitTypeDef GPIO_Init_Struct;
GPIO_Init_Struct.GPIO_Mode=GPIO_Mode_AF_PP; //TXD为复用推挽输出
GPIO_Init_Struct.GPIO_Pin=GPIO_Pin_9; //9号为TXD
GPIO_Init_Struct.GPIO_Speed=GPIO_Speed_50MHz; //这个随意
GPIO_Init(GPIOA,&GPIO_Init_Struct);
GPIO_Init_Struct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //RXD为浮空输入
GPIO_Init_Struct.GPIO_Pin=GPIO_Pin_10; //10号为RXD
GPIO_Init(GPIOA,&GPIO_Init_Struct);
USART_InitTypeDef Usart_Init_Struct;
Usart_Init_Struct.USART_BaudRate=9600; //波特率
Usart_Init_Struct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //无硬件控制流
Usart_Init_Struct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //收发模式
Usart_Init_Struct.USART_Parity=USART_Parity_No; //无校验
Usart_Init_Struct.USART_StopBits=USART_StopBits_1; //停止位1位
Usart_Init_Struct.USART_WordLength=USART_WordLength_8b; //数据位8位
USART_Init(USART1,&Usart_Init_Struct);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断配置
NVIC_InitTypeDef nitd;
nitd.NVIC_IRQChannel=USART1_IRQn;
nitd.NVIC_IRQChannelCmd=ENABLE;
nitd.NVIC_IRQChannelPreemptionPriority=2;
nitd.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&nitd);
USART_Cmd(USART1,ENABLE);
}
uint8_t CO2_Data[6]; //存放接收到的数据包
void USART1_IRQHandler(void){
static uint8_t index=0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
CO2_Data[index]=USART_ReceiveData(USART1);
if(index==0&&CO2_Data[index]!=0x2C){ //固定数据包的开头
return;
}else{
if(++index>=6) index=0;
}
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除数据接收标志位
}
}
int main(void){
OLED_Init();
JW01_Init();
OLED_ShowString(1,1,"Hello World");
OLED_ShowString(2,1,"CO2 is ");
while(1){
if(CO2_Data[5]==(uint8_t)(CO2_Data[0]+CO2_Data[1]+CO2_Data[2]+CO2_Data[3]+CO2_Data[4])){
OLED_ShowNum(2,8,CO2_Data[1]*256+CO2_Data[2],5);
}else{
OLED_ShowString(2,8,"ERROR");
}
OLED_ShowHexNum(3,1,CO2_Data[0],2);
OLED_ShowHexNum(3,4,CO2_Data[1],2);
OLED_ShowHexNum(3,7,CO2_Data[2],2);
OLED_ShowHexNum(4,1,CO2_Data[3],2);
OLED_ShowHexNum(4,4,CO2_Data[4],2);
OLED_ShowHexNum(4,7,CO2_Data[5],2);
}
}
上面代码涉及的OLED的驱动代码用的是b站江科大自化协的,没有OLED的小伙伴可以使用串口通信打印到电脑的串口助手上,不过只能用USART2了。因为JW01用了USART1。
接线方面的话就是把JW01的TXD接到GPIOA的10号引脚。
然后ST-Link的5V接出来给JW01供电,JW01的GND也和STM32共地(因为供电源一样)。