《物联网 - 机智云开发笔记》第2章 设备驱动开发
开发板:GoKit3开发板(STM32F103)
在上一章节,笔者带领大家已经将机智云平台玩起来,本节内容讲带领大家经进一步开发。
在开始讲解之前,有必要先了解的机智云的平台架构。
从上面的架构图可以看到,对于一般的开发者而言,我们重点要关注的就是设备端,也就是开发的重点,要想将各个传感器和模组与云端进行数据交互,那就必须在设备端实现数据的采集以及设备的控制,这些实现就需要开发者去完成。
因此本文将代码大家基于STM32实现相应的驱动开发。
2.1机智云产品创建与开发
这部分内容就不在赘述了,和上一章节内容一样,只是笔者这里的新建了很多数据点,当然这里需要根据自己的实际情况来配置,GoKit3的扩展板有丰富的资源,红外探测器,可编程全彩 RGB灯,可编程电机,温湿度传感器,三个可编程按键等,笔者这里选取RGB灯、温湿度、电机为例进行讲解。
因此产品信息对应配置如下:
然后关联相关的应用。
然后构建相应的应用即可。
云端和客户端比较简单。
2.2设备驱动开发
2.2.1硬件方案生成与配置
在设备开发之前,还需要在云端生成相应的应用方案。
将生成的硬件方案下载下来后,即可进行相应接口的初始化。
相应的硬件接口如下所示:
硬件 | 接口 |
---|---|
RGB LED | PB8, PB9 |
温湿度传感器 | PB3 |
可编程电机 | PB4, PB5 |
打开STM32F103C8x.ioc,将PA0, PB8, PB9, PB3分别配置成输出模式。
可编程电机使用PWM控制,因此是打开相应的PWM通道。
然后生成相应的初始化代码。
接下来就针对上述硬件进行开发。
2.2.2 RGB驱动开发
GoKit3的扩展板上设计了一个 RGB灯,通过 P9813 驱动,P9813芯片采用 CMOS 工艺,提供三路恒流驱动及 256 级灰度调制输出,可显示多达256256256 种颜色,性能优良,可视效果分明。其驱动电路如下。
其中CIN和DIN分别是时钟线和数据线。
P9813模块通讯采用双线传输方式(DATA、 CLK),其通信方式与 I2C 通信类似。 P9813芯片具体通信协议及时序如图所示。
① 前32 位“0”为起始帧,在Cin 上升沿时打入,并且时序DIN要先于CIN;
② 标志位为两个“1”;
③ 校验数据“B7’”与“B6’”为蓝色灰度数据的“B7”与“B6”的反码;
④ 灰度级数据要高位先入,并且是蓝绿红顺序。
从图可以看到通讯协议还是比较简单。
32位0起始信号: 0000 0000 0000 0000 0000 0000 0000 0000
第一点32位灰度数据: 11B7’B6’ G7’G6’R7’R6’ xxxx xxxx xxxx xxxx xxxx xxxx
第二点32位灰度数据: 11B7’B6’ G7’G6’R7’R6’ xxxx xxxx xxxx xxxx xxxx xxxx
首先是发送 32 位“0”的起始帧数据,然后接着发送 32 位的灰度数据,而这 32 位的灰度数据中分为 3 部分组成,包括“标志位”、“校验数据”、以及“24 位的 RGB 数据”。其中高两位(bit31 和 bit30)为“1”的标志位,bit29 ~ 24 位为 BGR 三种颜色灰度数据高两位(B7、 B6、 G7、 G6、 R7、 R6)的反码,反码就是正常颜色值的取反值,最后 bit23~0 位为 BGR 共 24 位的灰度数据。灰度级数据传输时需高位先入,并且按照蓝绿红(BGR)顺序发送。在通讯过程中时钟线和数据线时间建议工作参数如表所示。
参数 | 符号 | 范围 | 单位 |
---|---|---|---|
数据时钟频率 | FCLK | 0-8 | mhz |
时钟高电平宽度 | TCLKH | >30 | ns |
时钟低电平宽度 | TCLKL | >30 | ns |
数据建立时间 | TSETUP | >10 | ns |
数据保持时间 | THOLD | >5 | ns |
根据上述P9813的时序要求,时钟高低电平的最小延时是30ns,因此生成时钟的函数如下:
/*** @brief generation clock* @param None* @retval None*/
void clk_produce(void)
{SCL_LOW; // SCL=0delay_us(50);SCL_HIGH; // SCL=1delay_us(50);
}
接下来就是RGB控制的核心代码:
/*** @brief invert the grey value of the first two bits * @param dat* @retval tmp*/
uint8_t take_anti_code(uint8_t dat)
{uint8_t tmp = 0;tmp=((~dat) & 0xC0)>>6;return tmp;
}/*** @brief send gray data* @param dx* @retval None*/
void dat_send(uint32_t dx)
{uint8_t i;for (i=0; i<32; i++){if ((dx & 0x80000000) != 0){SDA_HIGH; // SDA=1;}else{SDA_LOW; // SDA=0;}dx <<= 1;clk_produce(); }
}/*** @brief data processing* @param r, g ,b* @retval None*/
void data_deal_with_and_send(uint8_t r, uint8_t g, uint8_t b)
{uint32_t dx = 0;dx |= (uint32_t)0x03 << 30; // The front of the two bits 1 is flag bitsdx |= (uint32_t)take_anti_code(b) << 28;dx |= (uint32_t)take_anti_code(g) << 26;dx |= (uint32_t)take_anti_code(r) << 24;dx |= (uint32_t)b << 16;dx |= (uint32_t)g << 8;dx |= r;dat_send(dx);
}
take_anti_code()函数实现了反转 RGB 灰度数据前高两位 bit7 和 bit6。
dat_send()函数,该函数实现发送灰度数据,从高位开始发送,共发送 32 位的数据。
data_deal_with_and_send()函数,该函数实现了通讯协议中 32bit 灰度数据的组成,先把 bit31 和 bit30 位设置为“1”标志位,接着让 bit29 ~ 24 位按 B、 G、 R 顺序反转BGR 三组灰度数据的高两位(B7、 B6、 G7、 G6、 R7、 R6),然后剩余的 bit23~0 也是按B、 G、 R 的顺序拼接 BGR 三组的 8 位灰度数据, 32bit 灰度数据组成完成后,最后调用dat_send()函数发送 32bit 数据。
最后就是RGB的初始化和控制了。
/*** @brief rgb init* @param r, g ,b* @retval None*/
void rgb_led_init(void)
{send_32_zero();data_deal_with_and_send(0,0,0); data_deal_with_and_send(0,0,0);
}/*** @brief rgb reg control* @param r, g ,b* @retval None*/
void led_rgb_control(uint8_t r, uint8_t g, uint8_t b)
{send_32_zero();data_deal_with_and_send(r, g, b);data_deal_with_and_send(r, g, b);
}
rgb_led_init()函数用于RGB的初始化,
led_rgb_control()函数实现 RGB 灯颜色显示,通过输入 R、 G、 B 颜色值数据(范围 0~255)就可以显示我们需要的颜色。
2.2.3直流电动驱动开发
在物联网中,电机的用处有很多,在智能家居系统中可以控制窗帘,在自动灌溉系统可以控制蓄水引擎等场景,GoKit3采用 L9110 进行驱动。
L9110芯片有两个 TTL/CMOS兼容电平的输入,具有良好的抗干扰性;两个输出端能直接驱动电机的正反向运动,它具有较大的电流驱动能力,每通道能通过 800mA 的持续电流,峰值电流能力可达 1.5A;同时它具有较低的输出饱和压降;内置的钳位二极管能释放感性负载的反向冲击电流,使它在驱动继电器、 直流电机、 步进电机或开关功率管的使用上安全可靠,L9110输入输出波形如下。
本文将使用PWM驱动L9110,从而控制直流电机,其硬件电路如下:
L9110控制电机速度和方向非常简单,按L9110输入输出波形,只要向输入端IA/IB输入高电平则为转动,IA 正转,IB为反转。
速度是通过调幅PWM 信号进行控制,也就是对IA/IB 写入 1~255 的速度范围则可控制电机的转速。
我们这里使用TIM3来输出PWM,关于定时器的使用,请参看笔者博文:
定时器
PWM
定时器的溢出时间如下:
Tout=1/(Tclk/(psc+1))∗(arr+1)Tout=1/(Tclk/(psc+1))*(arr+1)Tout=1/(Tclk/(psc+1))∗(arr+1)
根据前文的配置
这里 arr=7199,psc=9,Tclk=72Mhzarr=7199, psc=9, Tclk=72Mhzarr=7199,psc=9,Tclk=72Mhz,因此
Tout=1/(Tclk/(psc+1))∗(arr+1)=0.1msTout=1/(Tclk/(psc+1))*(arr+1)=0.1msTout=1/(Tclk/(psc+1))∗(arr+1)=0.1ms
TIM3设置的分频系数是9,通过分频后得到的时钟是7.2MHz。定时器周期设置的是7199,因此产生中断的频率为:7.2MHz/7200=0.1KHz7.2MHz/7200=0.1KHz7.2MHz/7200=0.1KHz。
TIM3的脉冲计数器 TIMx_CNT 上限设置为7199, TIMx_CCR 的值时,就会在通道中输出低电平,反之亦然,因此只需要改变TIMx_CCR,就能改变PWM的占空比,从而控制电机的转动。
void motor_control(uint8_t m1,uint8_t m2)
{uint16_t temp = (MOTOR_ARR + 1) / MOTOR_MAX;__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, m1 * temp);__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, m2 * temp);}
motor_control()函数有两个参数,分别控制IA 和IB,只需要设置不同的数值即可。
2.2.4 DHT11驱动开发
DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。
关于DHT11的详细讲解请参看笔者博文:
温湿度传感器DHT11
DHT11 模块的数据管脚用于 MCU 与 DHT11 之间的通讯和同步,采用单总线数据格式,一次通讯时间 4ms 左右,一次完整的数据传输为 40bit,高位先出,数据分小数部分和整数部分,具体格式是:
8bit 湿度整数数据+8bit 湿度小数数据+8bi 温度整数数据+8bit 温度小数数据 +8bit校验和数据传送正确时校验和数据等于 8bit 湿度整数数据+8bit 湿度小数数据+8bi 温度整数数据+8bit 温度小数数据所得结果的末 8 位。
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。例如,某次从 DHT11 读到的数据如下所示。
由以上数据就可得到湿度和温度的值,计算方法:
湿度= byte4 . byte3=45.0 (%RH)
温度= byte2 . byte1=28.0 ( ℃)
校验= byte4+ byte3+ byte2+ byte1=73(=湿度+温度)(校验正确)
可以看出, DHT11 的数据格式是十分简单的, DHT11 和 MCU 的一次通信最大为 3ms 左右,建议主机连续读取时间间隔不要小于 100ms。
下面,介绍一下 DHT11 的传输时序。
1.主机与DHT11通讯流程
主机MCU 发送一次开始信号后,DHT11 从低功耗模式转换到高速模式,等待主机开始信号结束后,即:拉低数据线,保持至少 18ms时间,然后拉高数据线 20~40us时间。DHT11 发送响应信号,送出 40bit 的数据,并触发一次信号采集,用户可选择读取部分数据。正常情况, DHT11 会拉低数据线,保持80us时间,作为响应信号,然后 DHT11 拉高数据线,保持80us时间后,开始输出数据。DHT11 接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11 不会主动进行温湿度采集。采集数据后转换到低速模式。
2.主机复位信号和 DHT11 响应信号
总线空闲状态为高电平,主机把总线拉低等待 DHT11 响应,主机把总线拉低必须大于 18 毫秒,保证 DHT11 能检测到起始信号。DHT11 接收到主机的开始信号后,等待主机开始信号结束,然后发送 80us 低电平响应信号。主机发送开始信号结束,延时等待 80us 后,读取 DHT11 的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高,如图中黑色线所示
总线为低电平,说明 DHT11 发送响应信号,DHT11 发送响应信号后,再把总线拉高 80us,准备发送数据,每一 bit 数据都以 50us 低电平时隙开始,高电平的长短定了数据位是 0 还是 1。格式如图中灰色线所示。如果读取响应信号为高电平,则 DHT11 没有响应,请检查线路是否连接正常。当最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态。
3.数字‘ 0’信号表示方法
数字 0 信号表示方法如图所示
4.数字‘ 1’信号表示方法
数字 1 信号表示方法如图所示
这里我们可以看出,数字“0”和数字“1”不同的地方在于高电平的时间不同,这也是读取数据的关键所在。
DHT11模块相关电路途如下图所示:
传感器模块是单总线通信,单总线通常要求外接一个上拉电阻,这样,当总线闲置时,总线上始终是高电平。
/*** @brief 从DHT11读取一个字节,MSB先行* @param None* @retval byte*/
static uint8_t dht11_read_byte ( void )
{uint8_t i, byte=0;for(i=0;i<8;i++) { /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/ while(DHT11_Dout_IN() == GPIO_PIN_RESET);/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,*通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 */delay_us(40); //延时x us 这个延时需要大于数据0持续的时间即可 if(DHT11_Dout_IN()==GPIO_PIN_SET)/* x us后仍为高电平表示数据“1” */{/* 等待数据1的高电平结束 */while(DHT11_Dout_IN()==GPIO_PIN_SET);byte|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行 }else // x us后为低电平表示数据“0”{ byte&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行}}return byte;
}
DHT11 接收到主机的开始信号后,等待主机开始信号结束,然后发送 80us 低电平响应信号。主机发送开始信号结束,延时等待 80us 后, DHT11 开始输出数据。每一 bit 数据都以 50us 低电平时隙开始,高电平的长短定了数据位是 0 还是 1。
2.2.5应用开发
根据上一章的讲解,完成了底层驱动后,用户只需要调用相应的 API 接口或添加相应的逻辑处理即可。
需要开发的部分为:
A. 下行处理: LED 灯开关、电机转速控制。
B. 上行处理: 温湿度数据采集。
C. 配置处理: 配置入网及恢复出厂设置。
下面一一将讲解。
首先是初始化外设,在 Gizwits 目录下的 gizwits_product.c 文件中userInit()函数中。
void userInit(void)
{memset((uint8_t*)¤tDataPoint, 0, sizeof(dataPoint_t));delay_init(72); // 延时 初始化rgb_led_init(); // RGB LED initdht11_init(); //init dht11motor_init(); // 电机初始化motor_status(0); // 电机转速初始化currentDataPoint.valueLED_OnOff = 0;currentDataPoint.valueMotor_Speed = 0;currentDataPoint.valueTemperature = 0;currentDataPoint.valueHumidity = 0;}
在 Gizwits 目录下的 gizwits_product.c 文件中的gizwitsEventProcess()函数中处理相应事件。
int8_t gizwitsEventProcess(eventInfo_t *info, uint8_t *gizdata, uint32_t len)
{uint8_t i = 0;dataPoint_t *dataPointPtr = (dataPoint_t *)gizdata;moduleStatusInfo_t *wifiData = (moduleStatusInfo_t *)gizdata;protocolTime_t *ptime = (protocolTime_t *)gizdata;#if MODULE_TYPEgprsInfo_t *gprsInfoData = (gprsInfo_t *)gizdata;
#elsemoduleInfo_t *ptModuleInfo = (moduleInfo_t *)gizdata;
#endifif((NULL == info) || (NULL == gizdata)){return -1;}for(i=0; i<info->num; i++){switch(info->event[i]){case EVENT_LED_OnOff:currentDataPoint.valueLED_OnOff = dataPointPtr->valueLED_OnOff;GIZWITS_LOG("Evt: EVENT_LED_OnOff %d \n", currentDataPoint.valueLED_OnOff);if(0x01 == currentDataPoint.valueLED_OnOff){//user handleled_rgb_control(0, 254, 0);}else{//user handle led_rgb_control(0, 0, 0);}break;case EVENT_Motor_Speed:currentDataPoint.valueMotor_Speed = dataPointPtr->valueMotor_Speed;GIZWITS_LOG("Evt:EVENT_Motor_Speed %d\n",currentDataPoint.valueMotor_Speed);//user handlemotor_status(currentDataPoint.valueMotor_Speed);break;case WIFI_SOFTAP:break;case WIFI_AIRLINK:break;case WIFI_STATION:break;case WIFI_CON_ROUTER:break;case WIFI_DISCON_ROUTER:break;case WIFI_CON_M2M:break;case WIFI_DISCON_M2M:break;case WIFI_RSSI:GIZWITS_LOG("RSSI %d\n", wifiData->rssi);break;case TRANSPARENT_DATA:GIZWITS_LOG("TRANSPARENT_DATA \n");//user handle , Fetch data from [data] , size is [len]break;case WIFI_NTP:GIZWITS_LOG("WIFI_NTP : [%d-%d-%d %02d:%02d:%02d][%d] \n",ptime->year,ptime->month,ptime->day,ptime->hour,ptime->minute,ptime->second,ptime->ntp);break;case MODULE_INFO:GIZWITS_LOG("MODULE INFO ...\n");#if MODULE_TYPEGIZWITS_LOG("GPRS MODULE ...\n");//Format By gprsInfo_t#elseGIZWITS_LOG("WIF MODULE ...\n");//Format By moduleInfo_tGIZWITS_LOG("moduleType : [%d] \n",ptModuleInfo->moduleType);#endifbreak;default:break;}}return 0;
}
在 user 目录下gizwits_product.c文件中的 userHandle()函数中实现传感器数据采集,用户只需并将采集到的数值赋值给对应用户区的设备状态结构体数据位即可。
void userHandle(void)
{DHT11_Data_TypeDef DHT11_Data;uint8_t ret = 0;static uint32_t thLastTimer = 0;// 温湿度传感器数据获取if((gizGetTimerCount()- thLastTimer) > 2000) //上报间隔2S{ret = dht11_read_temp_and_humidity(&DHT11_Data);if(ret != 0){GIZWITS_LOG("Failedto read DHT11 [%d] \n", ret);}else{currentDataPoint.valueTemperature = DHT11_Data.temp_int;currentDataPoint.valueHumidity = DHT11_Data.humi_int;}thLastTimer = gizGetTimerCount();}}
值得注意的是,关于驱动的头文件需要包含到gizwits_product.c中,请根据实际情况添加。
最后编译完成后将固件下载到MCU中,最后打开App,即可控制相应的设备。
值得注意的是,不管使用WiFi还是使用4G,MCU端的代码都是意昂的,无需更改,App端只需绑定对应的通信设备。
最新后效果如下:
欢迎访问我的网站
BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎
欢迎订阅我的微信公众号
关注公众号[嵌入式实验楼]获取更多资源
《物联网 - 机智云开发笔记》第2章 设备驱动开发相关推荐
- 基于Cortex-A7架构的嵌入式linux ARM驱动开发<1>——字符设备驱动开发
一.什么是字符设备 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI, L ...
- linux设备驱动开发专业论坛www.linuxdriver.cn诚征斑竹
www.linuxdriver.cn诚征斑竹,一起繁荣Linux设备驱动开发专业论坛的人气,共同致力于提高中国工程师的Linux设备驱动开发水平,普及Linux设备驱动开发知识.[url]www.li ...
- 演绎另类黑客马拉松,机智云第二届中国智能硬件36小时开发大赛再现极速开发...
互联网的蓬勃发展,以硬件开发为内容的创客马拉松在世界范围内兴起,吸引了众多硬件达人参与,智能手机的风行让源自编码潮流的黑客马拉松在全球风靡,堪称程序员的"美国偶像",一群软件开发高 ...
- 物联网开发笔记(94)- 使用Micropython开发ESP32开发板之获取天气数据并用ILI9341屏幕显示
一.目的 这一节我们学习如何使用乐鑫的ESP32开发板通过心知天气API控获取实时天气的数据,并使用串口SPI控制ILI9341 液晶屏,显示出来. 二.环境 ESP32(固件:esp32-20220 ...
- 物联网开发笔记(64)- 使用Micropython开发ESP32开发板之控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程:控件显示
一.目的 这一节我们学习如何使用我们的ESP32开发板来控制ILI9341 3.2寸TFT-LCD触摸屏进行LVGL图形化编程:控件显示. 二.环境 ESP32 + ILI9341 3.2寸TFT-L ...
- 物联网开发笔记(77)- 使用Micropython开发ESP32开发板之使用MAX7219驱动控制8x8LED点阵模块(续)
一.目的 这一节我们继续学习如何使用我们的ESP32开发板控制带MAX7219驱动的8x8LED点阵模.我们使用库来显示,更加方便. 二.环境 ESP32 + MAX7219驱动的8x8LED点阵模块 ...
- 物联网开发笔记(68)- 使用Micropython开发ESP32开发板之使用官方工具esptool烧录
一.目的 这一节我们学习如何使用我们的ESP32开发板来烧录. 二.环境 ESP32 + esptool + Win10 + python3.10 + 其他工具. ESP32开发板购买地址: http ...
- 微信硬件平台结合机智云,实现微信控制硬件设备
随着微信公众号及微信小程序的崛起,手机app的缺点开始凸显出来,尤其像一些功能不多,画面也不用很绚的app,完全是可以用微信公众号或者小程序替代的.因此公司也打算往微信公众号这块发展,此篇文章将实现一 ...
- 《Linux设备驱动开发详解》学习笔记一
Linux设备驱动开发详解学习笔记<一> 书名:<Linux设备驱动开发详解>第二版 主机环境:Linux version 2.6.25-14.fc9.i686@Fedora ...
最新文章
- 【问题收录】Ubuntu(14.04)那些我遇到的各种事
- DPM2012保护sharepoint场
- ABAP代码自动完成实现原理
- BitMap位图与海量数据的理解与应用
- dubbo+zookeeper下生产者和消费者配置(基于springboot开发)
- jmeter录制脚本
- 转:This Android SDK requires Andr...ate ADT to the latest
- 山西大学量子计算机,山西大学贾晓军课题组:在量子网络的多个节点之间建立和存储确定性量子纠缠...
- 计算机单片机考试作弊检讨书,考试作弊检讨书(精选5篇)
- IETESTER ie10.local 下载
- activerecords php,ActiveRecord_帮助文档_Thinkphp手册 | 码农网
- java如何将日期转化成字符串,Java将日期转换为字符串
- android解决kotlin问题Expecting member declaration
- 知乎上这个程序员火了。。。
- mpls接收udp并处理
- 大商创小程序前端_正版授权风暴!大商创商城系统开启免费送授权活动
- JUSTCTF2020 新生赛(校内)wp
- Vue3.0由单页面应用改为多页面开发
- 阿里云nginx安装
- vue+elementui封装下载base64文件流,下载模板。