想做一个能够远程检测温湿度的东西,这样家里放一个,外面放一个,早上一起来就能看到实时的温湿度,岂不美滋滋。
最初想法是单独买温湿度的传感器,像DHT11那样,然后在买一个无线模块像NRF24L01,然后再配个微控制器啥的,后来在淘宝搜着搜着就发现了WHT20这款神器,他本身就有无线功能,而且他的传感器是SHT20,精度更高。然后这样我就只要配个接收的微控制器,选来选去,觉得51用腻了,STM32有点大材小用了,Arduino Emmmm......不想用,就用了STM8S这么一个从来没用过的的微控制器。
先说下WHT20和他配套的接收器WSR。
一、WHT20
这是他官方介绍.(我只是不想打这么多字)
上面就有他的引脚,它可以通过串口在线设置,也可以通过WSR进行设置,他的VCC只能接3.3V左右的电压,实际测试,接5V虽然不会坏,但是发的都是乱码。
1.通过串口设置
通过串口设置时候,要把他的SET脚拉低,然后通过串口给他发送指令。(十六进制发送)
CC是开始标志;Time就是他的采集周期,可以设置1-65536s(占两个字节);NETID是他的网络号,只有WHT20和WSR的网络号相同时候才能通信,范围0-65535(两个字节);功率是设置他的发射功率(一个字节);波特率就是串口波特率(一个字节);Chose设置1时,他无线发射的同时也会通过串口发送,0就只无线发射(一个字节);Channel就是通信频道,有128个频道(一个字节);SUM是前面所有字节加起来,取低八位(一个字节),这校验和正确的时候,传感器才会返回值,不正确也能设置成功,不过不会返回....不知道算不算它的一个BUG;FF结束标志。
功率的对应表。
波特率对应表。
2.无线设置
短暂拉低KEY脚后拉高,此时就可以等待WSR的设置指令,此时传感器参数会恢复到默认状态NETID=0000,power=12dBm,channel=0x64.然后WSR就发射设置指令,按照如下格式。
只有指定的传感器ID才能接受设置参数,FFFFFF的话,会设置所有处于设置状态的传感器。
传感器设置成功后悔将参数保存到EEPROM中,然后发射返回值。
二、WSR
CS是片选,接低电平工作。SET是设置,拉低可通过串口对他进行设置。
1.串口设置自身参数。
拉低SET脚后,串口发送0XAA+0X5A+模块ID+NETID+0X00+POWER+0X00+波特率+0X00+Channel+0X00
+0X00+0X12+0X00+SUM
参数意义同WHT20。
2.设置传感器参数
WSR的SET拉低,通过串口发送AA5E指令,就可以设置处于设置状态的WHT20了
参数意义同WHT20,设置成功后就会返回AA5F指令
再就是他们之间的通讯协议了
这款传感器还是不错的,功耗真的挺低的,实测,12dBm的功率,采集周期7S,我用的锂电池,平均电流大概在1.6mA左右,也就是一个2000mah的锂电池,能用大概40多天。
STM8S
这个单片机不能用keil开发,只能用IAR和官方的STVD,网上的程序也是挺少的,开发它我也废了一番功夫。
我要用它的串口然后再用它驱动12864,我用的那一款引脚很少,所以12864我就用了SPI驱动方式。
说到底只是款单片机,看下时钟,看下各种片内外设寄存器就能用了呗。这个淘宝买的系统板,4块钱一个,真是便宜啊。。
官方的资料也是少之又少,他这板子用的是内部RC振荡电路提供时钟源,官方介绍说他因为出产,环境啥的不同,导致有差异。。。。就直接说不太准就行了呗.......,而且他是啥三级流水线,这个三级流水线意思就是呢...取指令,指令解码,指令执行交给了三个人去做,这样效率会快很多,但是
这也就造成一个问题,一条汇编指令,他的周期我不知道是多少了....
要想延时准还得用汇编........
volatile u8 fac_us=0;
void delay_init(u8 clk)
{
if(clk>16)fac_us=(16-4)/4;//24Mhz时,stm8大概19个周期为1us
else if(clk>4)fac_us=(clk-4)/4;
else fac_us=1;
}
void delay_us(u16 nus)
{
__asm(
"PUSH A \n" //压栈 1T
"DELAY_XUS: \n"
"LD A,fac_us \n" //倍延加载到累加器A 1T
"DELAY_US_1: \n"
"NOP \n" //1T
"DEC A \n" //1T
"JRNE DELAY_US_1 \n" //!=0 跳转(2T)到DELAY_US_1执行, =0 不跳转(1T).
"NOP \n" //1T
"DECW X \n" //1T
"JRNE DELAY_XUS \n" //!=0 跳转(2T)到DELAY_XUS 执行, =0 不跳转(1T).
"POP A \n" //出栈 1T
);
}
STM8S 通过SPI驱动LCD12864
void SPI_12864_Init(void)
{
SPI_Init(SPI_FIRSTBIT_MSB, SPI_BAUDRATEPRESCALER_2, SPI_MODE_MASTER,\
SPI_CLOCKPOLARITY_HIGH, SPI_CLOCKPHASE_2EDGE, \
SPI_DATADIRECTION_2LINES_FULLDUPLEX, SPI_NSS_SOFT, 0x00);
SPI_Cmd(ENABLE);
GPIO_Init(GPIOC,GPIO_PIN_4,\
GPIO_MODE_OUT_PP_HIGH_FAST );//定义LED的管脚的模式
GPIO_WriteHigh(GPIOC, GPIO_PIN_4);
}
void LCD_wr(u8 lcd_com,u8 lcd_data) //写入LCD数据或命令
{
u8 lcd_data_msb,lcd_data_lsb;
lcd_data_msb=0xf0&lcd_data;
lcd_data_lsb=(0x0f&lcd_data)<<4;
if(lcd_com==0) //写命令
{
while (SPI_GetFlagStatus( SPI_FLAG_TXE) == RESET); //等待 SPI 发送缓冲空
SPI_SendData(0xf8); // SPI 发送数据--命令指令--
}
if(lcd_com==1) //写数据
{
while (SPI_GetFlagStatus( SPI_FLAG_TXE) == RESET); //等待 SPI1 发送缓冲空
SPI_SendData(0xfa); //SPI1 发送数据--数据指令--
}
delay_ms(1);
while(SPI_GetFlagStatus( SPI_FLAG_TXE) == RESET); //等待 SPI1 接收数据完毕
SPI_SendData(lcd_data_msb); //SPI1 发送高4位数据
delay_ms(1);
while(SPI_GetFlagStatus( SPI_FLAG_TXE) == RESET); //Wait for SPI1 Tx buffer empty
SPI_SendData(lcd_data_lsb); // SPI1 发送低4位数据
delay_ms(1);
}
void initlcd(void) //LCD初始化
{
LCD_wr(0,0x30); //30---基本指令动作
delay_ms (4);
LCD_wr(0,0x01); //清屏,地址指针指向00H
delay_ms (3);
LCD_wr(0,0x06); //光标的移动方向
delay_ms (1);
LCD_wr(0,0x0c); //开显示,关游标
delay_ms (1);
}
//LCD显示字符串
//X Y起始坐标
//*ptr 字符串地址 dat 字符串长度
//返回值 无
void LCD_showstring(u8 X,u8 Y,u8 *ptr,u8 dat)
{
u8 i;
switch(X) //判断第几行
{
case 0:Y|=0x80;break;
case 1:Y|=0x90;break;
case 2:Y|=0x88;break;
case 3:Y|=0x98;break;
default:break;
}
LCD_wr(0,Y); //发送起始地址
for (i=0;i<dat;i++)
{
LCD_wr(1,*ptr++);
}
}
//LCD显示数字(包括负数)
//X,Y 数字显示起始地址
void LCD_shownum(u8 X,u8 Y,char num)
{
switch(X) //判断第几行
{
case 0:Y|=0x80;break;
case 1:Y|=0x90;break;
case 2:Y|=0x88;break;
case 3:Y|=0x98;break;
default:break;
}
LCD_wr(0,Y);
if(num>=128) //大于128代表负数
{
LCD_wr(1,0x2D); //显示‘-’
num=num&0x7f; //最高位置零
LCD_wr(1,num/10+0x30);//取十位
LCD_wr(1,num%10+0x30);//取个位
}
else if(num<128)
{
LCD_wr(1,(num)/10+0x30);
LCD_wr(1,(num)%10+0x30);
}
}
串口相关程序
void Uart_Init(void)
{
UART1_DeInit();
UART1_Init((u32)9600, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, \
UART1_PARITY_NO , UART1_SYNCMODE_CLOCK_DISABLE , UART1_MODE_TXRX_ENABLE);
UART1_ITConfig(UART1_IT_RXNE_OR,ENABLE );
UART1_Cmd(ENABLE );
}
void UART1_SendByte(u8 data)
{
UART1_SendData8((unsigned char)data);
/* Loop until the end of transmission */
while (UART1_GetFlagStatus(UART1_FLAG_TXE) == RESET);
}
void UART1_SendString(u8* Data,u16 len)
{
u16 i=0;
for(;i<len;i++)
UART1_SendByte(Data[i]);
}
u8 UART1_ReceiveByte(void)
{
u8 USART1_RX_BUF;
while (UART1_GetFlagStatus(UART1_FLAG_RXNE) == RESET);
USART1_RX_BUF=UART1_ReceiveData8();
return USART1_RX_BUF;
}
void CLR_BUF() //清空串口接收缓存
{
point=0;
memset( RxBuffer,0,20);
}
#pragma vector=20
__interrupt void UART1_RX_IRQHandler(void)
{
u8 Res;
static u8 start;
//LCD_display(0,0,"hello1",8);
if(UART1_GetITStatus(UART1_IT_RXNE )!= RESET) /*接收中断(接收到的数据必须是0x0d 0x0a结尾)*/
{
Res =UART1_ReceiveData8();/*(USART1->DR);读取接收到的数据,当读完数据后自动取消RXNE的中断标志位*/
if(Res==0xAA) {start=1;} //判断数据是否开始
if(Res==0x5B) {cmd_responce=1;} //判断是否是命令
if(Res==0xFF) {start=0;usart_flag=1;} //判断是否结束
if(start) RxBuffer[point++]=Res; //读取命令OR数据
}
}
WSR相关函数
void wsr_Init(void)
{
GPIO_Init(GPIOA,GPIO_PIN_1,\
GPIO_MODE_OUT_PP_HIGH_FAST );//SET脚接A1
GPIO_Init(GPIOA,GPIO_PIN_2,\
GPIO_MODE_OUT_PP_HIGH_FAST );//CS脚接A2
GPIO_WriteLow(GPIOA, GPIO_PIN_2);
GPIO_WriteHigh(GPIOA, GPIO_PIN_1);
}
//WSR设置函数
//NETID:网络号 power:功率 channel:频道 baud:波特率
//返回参数:0:设置不成功
// 1:设置成功
u8 wsr_set(u8 NETID,u8 power,u8 channel,u8 baud)
{
u8 i;
u8 cmd[18]={0xAA,0X5A,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X04,0X00,0X64,0X00,0X00,0X00,0X12,0X00,0X00};//命令数组
cmd[5]=NETID;
cmd[7]=power;
cmd[9]=baud;
cmd[11]=channel;
for(i=0;i<17;i++)
cmd[17]=cmd[17]+cmd[i]; //添加校验位
GPIO_WriteLow(GPIOA, GPIO_PIN_1); //SET L
delay_ms(1);
UART1_SendString(cmd,18); //发送命令
//delay_ms(1);
while(!cmd_responce); //等待命令回复
cmd_responce=0;
GPIO_WriteHigh(GPIOA, GPIO_PIN_1); //SET H
delay_ms(3);
if(RxBuffer[5]==NETID&&RxBuffer[7]==power&&RxBuffer[9]==baud&&RxBuffer[11]==channel) //判断是否设置成功
{
CLR_BUF();
return 1;
}
CLR_BUF();
return 0;
}