程序代码及PCB
程序架构
我们使用嵌入式实时系统中的UCOS III作为框架,进行程序的开发。 UCOS III资料:UCOSIII简介
声光报警
电压检测
串口屏
AT24C02
阿里云
等待电压值更新
更新上、下限
阻塞等待完成检测
完成8个通道检测
检测电压范围是否正常
完成检测,循环写AT24C02
同时,发送数据至串口屏
写入低8位,AT24C02需5ms处理
写入高8位,AT24C02需5ms处理
发布话题至阿里云,从而更新数据
进入阻塞,等待下一次检测完成
声光报警
电压检测
串口屏
AT24C02
阿里云
连接阿里云
阿里云
在本课设中,我们使用的是阿里云的物联网平台。阿里云物联网平台是一个集成了设备管理、数据安全通信和消息订阅等能力的一体化平台。向下支持连接海量设备,采集设备数据上云;向上提供云端API,服务端可通过调用云端API将指令下发至设备端,实现远程控制。 详细介绍:什么是物联网平台
wifi
WiFi模块使用ESP8266,型号为安信可的ESP-12S,使用AT指令配置相关的功能。 esp8266资料:安信可ESP8266简介 ESP8266有三种工作模式: 1.Station (客户端模式)模块相当于一个客户端,可以链接到其他路由器发出的WIFI信号。主要应用在网络通信中。 2.AP (接入点模式)模块本身相当于一个路由器,其他设备可链接到该模块发送的信号,主要应用在主从设备的主机部分。 3.Station+AP (两种模式共存)就是说模块可以当成一个设备(client)连接区域网内的路由,也可以设置成是一个路由(sever),也可以既作为局域网里面的client同时又是其他client的sever。 本课设中使用STA模式,并连接WiFi,AT指令配置如下
AT+CWMODE=1
AT+RST
AT+CWJAP="iQOO Z1x","12345678"
mqtt
MQTT(消息队列遥测传输) 是基于 TCP/IP 协议栈而构建的支持在各方之间异步通信的消息协议。MQTT在空间和时间上将消息发送者与接收者分离,因此可以在不可靠的网络环境中进行扩展。虽然叫做消息队列遥测传输,但它与消息队列毫无关系,而是使用了发布和订阅(Pub/Sub)的模型。 MQTT 是一种轻量级的、灵活的网络协议,致力于为 IoT 开发人员实现适当的平衡:
- 这个轻量级协议可在严重受限的设备硬件和高延迟/带宽有限的网络上实现。
- 它的灵活性使得为 IoT 设备和服务的多样化应用场景提供支持成为可能。
详细介绍:MQTT 入门介绍 使用AT指令连接阿里云 连接指令参考
AT+CIPSNTPCFG=1,8,"ntp1.aliyun.com"
AT+MQTTUSERCFG=0,1,"","","",0,0,""
AT+MQTTUSERNAME=0,"voltmeter&a1LF9tJmi7L"
AT+MQTTPASSWORD=0,"00D54361109FD2F1F4537F928C0CBB6EEA81E253"
AT+MQTTCLIENTID=0,"12345|securemode=3\,signmethod=hmacsha1\,timestamp=789|"
AT+MQTTCONN=0,"a1LF9tJmi7L.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,1
使用AT指令对阿里云发布话题
AT+MQTTPUB=0,"/sys/a1LF9tJmi7L/voltmeter/thing/event/property/post","{\"method\":\"thing.service.property.set\"\,\"id\":\"2012934115\"\,\"params\":{\"ch2\":4.7231}\,\"version\":\"1.0.0\"}",1,0
电压采集
ADC
我们所使用的stm32f411拥有1个ADC,12通道,其拥有12位的分辨率,对于3.3v,可以达到约0.00081v的精度。我们在程序中配置通道0-8进行测量,配置采样速率25MHz,每个规则序列中只包含一个通道,并配置连续扫描。 每次采样时会采集10个值,经过冒泡法排序后,去除最小、最大值,并计算平均值从而达到滤波的效果。
void my_sort(u16 *buf,u8 num)
{
u8 i,j,flag=1;
u16 k;
for(i=0;i<9;i++)
{
for(j=0;j<9-i;j++)
{
flag = 0;
if(buf[j]>buf[j+1])
{
k = buf[j];
buf[j] = buf[j+1];
buf[j+1] = k;
flag = 1;
}
}
if(flag==0)
{
break;
}
}
}
static void ADC_task ( void * p_arg )
{
u16 buf[10];
u8 i=0;
OS_ERR err;
while(1)
{
OSSemPend(&STAER_ADC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err);
for(i=0;i<9;i++)
{
Get_Adc(channel_list[i].ADC_channels,buf);
my_sort(buf,10);
channel_list[i].current=my_average(buf,1,8)/4096*3.3*10000;
if(i!=8)
bouns_light(&channel_list[i]);
}
OSSemPost(&END_ADC_SEM,OS_OPT_POST_1,&err);
OSTimeDlyHMSM(0, 0, 0, 5, 0, &err);
}
}
同时我们使用优质的LDO芯片XC6203为stm32及adc供电,保证了其基准电压的稳定。 stm32的adc详细介绍:STM32—ADC详解
分压电路
由于stm32的ADC只能测量0-3.3v电压,我们使用精密电阻(精度0.1%)进行分压,并且增加了一个100nf的电容稳定电压,从使8个通道的特性相差较小。分压电阻为1.2M和1M,经过分压,stm32可以测量0-6.05V电压
声光报警
蜂鸣器及LED
蜂鸣器
我们使用5V的有源蜂鸣器,保证了声音足够大。但是stm32无法输出5V电压,于是我们将IO口配置为开漏输出,通过上拉电阻实现5v输出。
void BEEP_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
BEEP_OFF();
}
LED
我们通过相关的电路设计,实现通过stm321的一个IO口控制两个LED的亮灭。正常时,stm32的IO配置为高阻态,此时红灯、绿灯都不亮;超过上限,IO口配置为推挽输出、低电平,此时红灯亮;低于下限,IO配置为推挽输出、高电平,此时绿灯亮。
void LED_set(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pinx,unsigned char state)
{
GPIO_InitTypeDef GPIO_InitStructure;
if(state==0)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
else if(state==1)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOx, &GPIO_InitStructure);
GPIO_SetBits(GPIOx,GPIO_Pinx);
}
else if(state==2)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOx, &GPIO_InitStructure);
GPIO_ResetBits(GPIOx,GPIO_Pinx);
}
}
串口屏
电压值的显示及上、下限的设置,我们使用串口屏,通过上位机制作其显示的界面并烧录。通过串口屏,我们可以设置相关的上下限、查看当前值及掉电保存的值。并且通过串口于stm32通讯
数据存储
AT24C02
尽管stm32用于内部flash,当时由于其读写寿命只有1万次左右。而我们的课设中以10Hz的速度更新9个通道值,每个电压值对应的存储块每秒读写就有10次,一分钟就有600次,所以我们选择EEPROM作为存储介质,AT24C02拥有30万次的读写寿命,再通过均衡读写,就可以实现课设的要求。 详细介绍:AT24C02 存储器FLASH,EEPROM介绍:FLASH和EEPROM的区别
static void AT24C02_task ( void * p_arg )
{
OS_ERR err;
u16 temp=0;
u8 i=0;
while(1)
{
OSSemPend(&END_ADC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err);
for(i=0;i<9;i++)
{
temp=channel_list[i].current;
screen_send_value(channel_list[i].screen_name_current,temp);
AT24CXX_WriteOneByte(channel_list[i].AT24C02_Palce,temp%256);
OSTimeDlyHMSM(0, 0, 0, 5, 0, &err);
AT24CXX_WriteOneByte(channel_list[i].AT24C02_Palce+1,temp/256);
OSTimeDlyHMSM(0, 0, 0, 5, 0, &err);
}
if(ch>0)
{
temp=channel_list[ch-1].current;
screen_send_value("chnael.x0.val",temp);
}
OSSemPost(&STAER_ADC_SEM,OS_OPT_POST_1,&err);
}
}
供电
供电一开始我们选择使用AMS117,将12V降至5v,但压差过大,AMS117发热较大,无法正常使用。或许可以考虑使用7805或LM2956进行压降。不过我们后来想了一下,不如直接用5V供电。至于stm32的3.3v供电,我们使用了LDO芯片XC6204
|