根据上述P9813的时序要求,时钟高低电平的最小延时是30ns,因此生成时钟的函数如下:
/**
* @Brief generation clock
* @param None
* @retval None
*/
void clk_produce(void)
{
SCL_LOW; // SCL=0
delay_us(50);
SCL_HIGH; // SCL=1
delay_us(50);
}
接下来就是RGB控制的核心代码:
/**
* @brief invert the grey value of the first two bits
* @param dat
* @retval tmp
*/
uint8_t take_anti_code(uint8_tdat)
{
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
*/
voiddata_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 isflag bits
dx |= (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输入输出波形如下。
L9110控制电机速度和方向非常简单,按L9110输入输出波形,只要向输入端IA/IB输入高电平则为转动,IA 正转,IB为反转。
速度是通过调幅PWM 信号进行控制,也就是对IA/IB 写入 1~255 的速度范围则可控制电机的转速。
我们这里使用TIM3来输出PWM,关于定时器的使用。
定时器3设置的分频系数是9,通过分频后得到的时钟是7.2MHz。定时器周期设置的是7199,因此产生中断的频率为:7.2MHz/7200=0.1KHz。
TIM3的脉冲计数器 TIMx_CNT 上限设置为7199, TIMx_CCR 的值时,就会在通道中输出低电平,反之亦然,因此只需要改变TIMx_CCR,就能改变PWM的占空比,从而控制电机的转动。
void motor_control(uint8_tm1,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, m1 * temp);
}
motor_control()函数有两个参数,分别控制IA 和IB,只需要设置不同的数值即可。
2.2.4 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 信号表示方法如图所示
这里我们可以看出,数字“0”和数字“1”不同的地方在于高电平的时间不同,这也是读取数据的关键所在。
DHT11模块相关电路途如下图所示:
传感器模块是单总线通信,单总线通常要求外接一个上拉电阻,这样,当总线闲置时,总线上始终是高电平。
/**
* @brief 从DHT11读取一个字节,MSB先行
* @param None
* @retval byte
*/
static uint8_t dht11_read_byte (void )
{
uint8_ti, 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先行
}
}
returnbyte;
}
DHT11 接收到主机的开始信号后,等待主机开始信号结束,然后发送 80us 低电平响应信号。主机发送开始信号结束,延时等待 80us 后, DHT11 开始输出数据。每一 bit 数据都以 50us 低电平时隙开始,高电平的长短定了数据位是 0 还是 1。
2.2.5应用开发根据上一章的讲解,完成了底层驱动后,用户只需要调用相应的 API 接口或添加相应的逻辑处理即可。
需要开发的部分为:
A. 下行处理: LED 灯开关、电机转速控制。
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 init
- dht11_init(); //init dht11
-
- motor_init(); // 电机初始化
- motor_status(0); // 电机转速初始化
- currentDataPoint.valueLED_OnOff = 0;
- currentDataPoint.valueMotor_Speed = 0;
- currentDataPoint.valueTemperature = 0;
- currentDataPoint.valueHumidity = 0;
- }
- 在 Gizwits 目录下的gizwits_product.c 文件中的gizwitsEventProcess()函数中处理相应事件。
- int8_tgizwitsEventProcess(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_TYPE
- gprsInfo_t *gprsInfoData = (gprsInfo_t *)gizdata;
- #else
- moduleInfo_t *ptModuleInfo = (moduleInfo_t *)gizdata;
- #endif
- if((NULL == info) || (NULL == gizdata))
- {
- return -1;
- }
- for(i=0; i<info->num; i++)
- {
- switch(info->event)
- {
- 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 handle
- led_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 handle
- motor_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_TYPE
- GIZWITS_LOG("GPRS MODULE...\n");
- //Format By gprsInfo_t
- #else
- GIZWITS_LOG("WIF MODULE...\n");
- //Format By moduleInfo_t
- GIZWITS_LOG("moduleType : [%d]\n",ptModuleInfo->moduleType);
- #endif
- break;
- default:
- break;
- }
- }
- return 0;
- }
- 在 user 目录下gizwits_product.c文件中的 userHandle()函数中实现传感器数据采集,用户只需并将采集到的数值赋值给对应用户区的设备状态结构体数据位即可。
- void userHandle(void)
- {
- DHT11_Data_TypeDef DHT11_Data;
- uint8_t ret = 0;
- staticuint32_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端只需绑定对应的通信设备。
最新后效果如下: