基于STM32F103的网络温度报警器-物联网.docx
基于STM32F103的网络温度报警器设计作品名:基于STM"FI(B的网络温度报警器设计陈华健贾从含时间:2023年6月P日目录:L引言12利用普通二极管PN结测试环境温度原理24 .UC/0S系球植65 文件系统的移植与文件系统根本函数的功能166IS'C=ck*'tI7!JtJfjA.*+*+*B*+»>*+*+*+*E+,B*+,*,*.*+*,.*+*,*,+*.*21 .引言近年来随着科技的飞速开展,嵌入式的应用正在不断深入,同时带动传统控制检测技术日益更新。在实时检测和自动控制的嵌入式应用系统中,嵌入式往往作为一个核心部件来使用,仅嵌入式方面知识是不够的,还应根据具体硬件结构软硬件结合,加以完善。本系统使用STM32F103实现了接收由上位机通过TCP协议发出的温度报警阈值信号,并存于SD片卡中.单片机利用普通二极管的PN结测试环境温度,每30s采集一次,将采集到的温度信息补充上时间时、分、秒、毫秒标注存储在存储芯片中。并将报警时的温度值与当前时间的温度进行比拟,当前温度大于阀值温度时,通过发光二极管或蜂鸣器报警。上位机通过TCP,向单片机发送"ReacUnfo.命令后,单片机能将SD卡中存储的所有数据发到PC机的串口助手中;数据格式美观、易懂。本系统采用普通二极管PN节的温度特性来测量环境温度不失为一种低本钱而又容易实现的环境温度测量方式.使用STM32自带的ADC模块进/降低了本钱和设计难度.采用大容量存储芯片可以长时采集环境数据,并且在采集到的温度补充上时间信息使数据更加可信,同时移植了文件系统方便文件在WlNDOWS下的读取和处理.本系统采用了无线传输的方式配合可靠的电源设备或太阳能设备可以在室外持续的传输回温度信息或其他的气象数据需配适宜当的传感器,减少了人工本钱,并且更加适应于野外大规模投放接点.2利用普通二极管PN结测试环境温度原理.温度是表示物体或环境冷热程度的一种物理量,而温度传感器是一种能将温度变化转换成电量变化的元器件。由于二极管制造工艺的特殊性,我们可以利用二极管的伏安特性来测量环境的温度,它的伏安特性如下列图众所周知,将PN结用外壳封装起来,并加上电极引线就构成了半导体二极管,即所谓的二极管。由P区引出的电极为阳极,由N区引出的电极为阴极,如下列图所示温度对二极管的性能有较大的影响,温度升高时,二极管的正向压降将减小,每增加IC,正向压降减小约2mV,因此可以使用这一特性来测量环境温度.由半导体理论可以得出,PN结所加端电压U与流过它的电流i的关系为:其中,Is为反向饱和电流,对于硅材料来说,Is约为IOpA;q为电子的电量,q=1.6*10的-9次方库伦;k是玻耳茨曼常数,k=1.38*10的-23次方J/K;T为绝对温度,kT/q可以用UT来代替,常温下,即T=300K时,UT约为26mV.对于足够大的电压,二极管方程可以近似写成那么,二极管两端的电压可以推导出为:因此温度的公式为:3器件的选择和芯片的介绍本系统采用了ST公司和高性能微控制器-STM32F130ZET6该微控制器具有512KROM以及62KRAM足以满足该工程的需求.本系统使用到的模块有:ENC28J60模块,0.96'OLED模块,SD卡模块,以及2个无线模块和USB-TTL模块。为了满足这些模块的供电需求另外自己用洞洞板做了AMS1117的稳压模块,以及采用德州仪器公司的TPS7333稳压芯片制作了稳压模块为无线模块提供稳定可靠的电源使数据的发送和接收更加稳定.AMSlIl7系列稳压芯片有可调版与多种固定电压版,设计用于提供IA输出电流且工作压差可低至Ik在最大输出电流时,AMS1117器件的压差保证最大不超过1.3V,并随负载电流的减小而逐渐降低.本系统采用的是输出3.3v的固定电压版本.电路图如下:TPS7333是由德州仪器公司研发生产的单通道线性稳压芯片具有单输出LDO.500mA,固定电压(3.3V)、集成SVS、低静态电流,性能十分稳定,输出电压纹波低。应用电路比拟简单,电路如下:4.UC/OS系统移植uCOS是一个微型的实时操作系统,包括了一个操作系统最根本的一些特性,如任务调度、任务通信、内存管理、中断管理、定时管理等。而且这是一个代码完全开放的实时操作系统,简单明了的结构和严谨的代码风格,非常适合初涉嵌入式操作系统的人士学习。很多人在学习STM32中,都想亲自移植一下uC/OS,而不是总是用别人己经移植好的。在我学习uC/OS的过程中,杳找了很多资料,也看过很多关于如何移植uC/OS到STM32处理器上的教程,但都不尽人意,主要是因为是时间比拟赶,无法静下心开好房学习,在一个月时间内完成STM32的学习以及UIP、文件系统的移植还是比拟辛苦和困难的。1 .首先需要从官网上下载UC/OS的源码,并且选择STM32F103ZET6,由于官方没有公布KEIL版本的工程只有IAR版本,所以需要进行一定的修改才可用于KEIL中。UC/OS的文件结构如下列图所示:2 .按照下列图的文件结构搭建uC/OS工程文件结构 把LED工程所在的文件夹先改名为:STM32+UCOS 在USER文件夹下新建includes.h头文件。按照之前给的uC/OS-n文件结构图,我们在工程的根目录下建立BSP文件夹、APP文件夹和UCOS-Il文件夹.BSP文蜂存放外设硬件驱动人APP文件夹存放应用软件任务UCoS-Il文件夹uC/OSF的相关代码 把USER文件夹下的led.h和led.c文件剪切到BSP文件夹里。在BSP文件夹里新建BSRc和BSRh文件. 在APP文件夹下建立app.h、app.c和app_cfg.h文件.拷贝uC/OS-II源代码附件那里的MicriumSoftwareEvalBoardsSTSTM32F103ZE-SKIAROS-PrObe-LCDos_cfg.h到此目录.把uCOS-11源代码附件那里的MicriumSoftwareuCOS-II下的Source文件夹复制到工程里刚刚新建的uCOS-n文件夹里.把MiCriUmSoftwareuCOS-11Portsarm-cortex-m3GenericIAR下的文件复制到工程uCOS-n文件夹中新建的Ports文件夹里.复制后,选中全部文件,右键属性-去掉只读属性一确定。如下列图添加deludepath3配置uC/OS-IIa.修改os_cfg.h:首先禁用信号量、互斥信号量、邮箱、队列、信号量集、定时器、内存管理,关闭调试模式:#defineOS_FLAG_EN0禁用信号量集#defineOS_MBOX_EN0禁用邮箱#defineOS_MEM_EN0禁用内存管理#defineOS_MUTEX_EN0禁用互斥信号量#defineOS_Q_EN0禁用队列#defineOS_SEM_EN0禁用信号量#defineOS_TMR_EN0禁用定时器#defineOS_DEBUG_EN0禁用调试b.修改oS-Cpu.h注释掉这三行voidOS_CPU_SysTickHandler(void);voidOS-CPU-SySTiCkInit(Void);INT32UOS-CPU-SySTiCkCIkFreq(Void);C.修改OS-CPU一c.c把OS_CPU_SysTickHandler(),OS-CPILSySTiCkInit()及如下列图的文件注葡岸d.修改OS-CPU_a.asm由于编译器的原因要将下面的PUBIC改为EXPORT:PUBLICOS_CPU_SR_Save;FunctionsdeclaredinthisfilePUBLICOS_CPU_SR_RestorePUBLICOSStartHighRdyPUBLICOSCtxSwPUBLICOSIntCtxSwPUBLICOS_CPU_PendSVHandlere.修改os_dbg.c将#defineOS_COMPILER_OPT_root改为#defineOS_COMPILER_OPT/_root修改startup_stm32flOx_hd.s因为本次移植是使用标准外设库CMSIS中startup_stm32fl0x_hd.s作为启动文件的,还没有设置CIS-CPU一SySTiCkHandIer.而startup_stm32flOx_hd.s文件中,PendSV中断向量名为PendSV_Handler,因此只需把所有出现PendSV_Handler的地方替换成OS-CPU-PendSVHandIer即可。编写includes.h#ifndef_INCLUDES_H_#define_INCLUDES_H_*include'stm32flx.h'#include"stm32flx一rcc.h"SySTiCk定时器相关#include,ucosji.h"uC/OS-lI系统函数头文件*include'BSP.h""与开发板相关的函数include"apph'"用户任务函数#include'led.h,LED驱动函数#endif/_INCLUDES_H_编写BSP.cinclude"indudesh'voidBSPJnit(Void)SystemInitO;/*配置系统时钟为72M*/SysTickJnitO;/*初始化并使能SysTick定时器*/1.ED-GPlcLConfig();*LED端口初始化*/voidSysTickJnit(Void)SySTiCkjZonfig(SystemFrequencwOS-TICKS-PER-SEC);/初始化并使能SySTiCk定时器Bsp.h#ifndef_BSP_H#define_BSP_HvoidSysTickJnit(Void);voidBSPJnit(Void);#endif/_BSP_H编写main.c#include,includes.h'staticOS_STKstartup_task_stkSTARTUP_TASK_STK_SIZE;定义栈intmain(void)BSPJnitO;OSlnit();OSTaskCreate(Task.LED1(void*)0,&startup_task_stkSTARTUP_TASK_STK_SIZE-l,STARTUP_TASK_PRIO);OSStart();return0;至此,UC/OS的移植已经完成,运行多任务只需在APRC里修改即可.眼于篇幅,一下不再赘述,详情请看源码.5文件系统的移植与文件系统根本函数的功能DSDIO配置与SD卡实现:a. SDIo接线如下列图所示:b. SDIo时钟设置:SDIo_CK时钟是通过PC12引脚连接到SD卡的是SDIO接口与SD卡用于同步的时钟。SDIO选配器挂载到AHB总线上,通过HCLK二分频输入到适配器得到SDI0_CK的时钟,这时SDlo_CK=HCLK/(2+CLKDIV).其中CLKDiV是SDlcLCLK(存放器)中的CLKDiV位。另外,SDI0-CK也可以由SDIOClk通过设置bypass模式直接得到,这时SDI0.CK=Sdioclk=Hclk0可以通过以下函数进行时钟配置SDIOJnit(&SDIO_InftStructure);对SD卡的操作一般是大吞吐量的数据传输,所以采用DMA来提高效率,SDIO采用的是DMA2中的通道4.在数据传输的时候SDIO可向DMA发出请求.c.SDI。协议驱动声明:由于原来没有了解过SD协议,又看到SDK)的驱动有2000多行,时间紧迫,感觉无从下手.故采用ST公司官方驱动。以下简要介绍所用到的函数的功能SDlO一SendCommand(&SDIO_CmdInitStrUCtUre);配置和发送命令SDIo通过CMD接收到响应后,硬件去除头尾的信息,把Commandindex保存到SDIO.RESPCMD存放器,把argumentfield内容保存存储到SDlo_RESPx存放器中。这两个值可以分别通过下面的库函数得到。SDlo-GetCommandReSPonSe();卡返回接磔IJ的命令SDlo一GetReSPonSe(SDlo一RESPI);/卡返回的argumentfield内容d.SDI0nit()函数:1)用6门。_:。19112m。11()进行SDIO的端口底层配置2)分别调用了SD_P0WerON()和SD_InitiaIizeCardsO函数,这两个函数共同实现了上面提到的卡检测、识别流程.3)调用SDl0而(&560血5也m小)库函数配置SDlO的时钟,数据线宽度,硬件流在读写数据的时候,开启硬件流是和很必要的,可以减少出错)4)调用SD_GetCardInfo(&SDCardInfo)获取sd卡的CSD存放器中的内容,在main函数里输出到串口的数据就是这个时候从卡读取得到的.5)调用SD-SeIectDeseIectO选定后面即将要操作的卡.6)调用SD-EnabIeWideBusOPeratiOn(SDI0_BusWide_4b)开启4bit数据线模式如果SDnit()函数能够执行完整个流程,并且返回值是SD.OK的话那么说明初始化成功,就可以开始进行擦除、读写的操作了。下面进入SD-POWeroN()函数,分析完这个函数大家就能了解SDIO如何接收、发送命令了.e.SDIO-Iint()中使用的函数:SD_PowerON函数:确保SD卡的工作电压和配置控制时钟SDJnitiaIizeCards:初始化所有的卡或者单个卡进入就绪状态2)FATFS文件系统的移植FATFS是面向小型嵌入式系统的一种通用的FAT文件系统.FATFS完全是由AISIC语言编写并且完全独立于底层的I/O介质.因此它可以很容易地不加修改嘘植到其他的处理器当中,如8051、PIC、AVR、SH、Z80、H8、ARM等。FATFS支持FATI2、FATI6、FAT32等格式,利用前面写好的SDIO驱动,把FATFS文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对已格式化的SD卡进行读写文件了.首先从官网下载FATFS源码,然后解压到工程文件中,并添加到工程中下面对FATFS的文件做说明:integer.h:是一些数值类型定义diskio.c:底层磁盘的操作函数,函数需要用户自己实现ff.c:独立于底层介质操作文件的函数,完全由ANSIC编写cc936.c:简体中文支持所需要添力口的文件,包含了简体中文的GBK和转换函数。ffconf.h:这个头文件包含了对文件系统的各种配置如需要支持简体中文要把-CoDE_PAGE的宏改成936并把上面的cc936.c文件参加到工程之中移植过程中要修改的文件1、将integer.h中有关BOOL的那句注释掉2、在ff.c文件的开头重新定义一个布尔变量,取名为bool,与stm32fl0x.h中的名字一样:同时在ff.c的第585行做如下修改:文件系统移植成功!下面介绍文件系统中的几个底层函数:a.文件系统初始化函数接口的实现DSTATUSdiskjnitialize(BYTEdrv*Physicaldrivenmuber(0.)*/)SD,ErrorStatus;*Supportsonlysingledrive*/if(drv)returnSTA.NOINIT;*SDInit7StatUS=SDInit。;if(Status!=SD.OK)returnSTA,NO1N;elsereturnRES_OK;这个函数调用了SDIO的SDnit()函数,返回成功或失败的参数.当文件系统调用到这个函数的时候,实际上是调用了SDnit()对SD卡进行初始化。b.扇区读取函数的实现:RESULTdiskead(BYTEdrv,*Physicaldrivenmuber(0.)*/BYTE*buff,*Databuffertostorereaddata*/DWORD sector,BYTE count*Sectoraddress(LBA)7*Numberofsectorstoread(1.255)*/if(count>1)SD_ReadMultiBlocks(buff,sector*BLOCK_SIZE,BLOCK.SIZE,count);*CheckiftheTransferisfinished*/SD-WaitReadOperation();循环查询dma传输是否结束*WaituntilendofDMAtransfer*/while(SD,GetStatusQ!=SD_TRANSFER_OK);elseSD_ReadBlock(buff,sector*BLOCK_SIZE,BLOCK_S1ZE);*CheckiftheTransferisfinished*/SD-WaitReadOperationQ;循环查询dma传输是否结束*WaituntilendofDMAtransfer*/while(SD_GetStatus()!=SD_TRANSFER_OK);returnRES_OK;此函数分为了2个局部,分为单块读取和多块读取数据,因为使用SD_ReadMuItiBIocks比SD_ReadBlock(速度要快所以参加了一判断函数来区分以增加系统的效率。由于文件系统都是以块512字节)为单位读写的所以只要提供512字节或者512*N字节的SD卡驱动即可。c.扇区写入函数的实现:DRESultdisk.write(BYTE drv,*Physicaldrivenmuber(0.)*/const BYTE *buff,* Data to be written */DWORD sector,* Sector address (LBA) */BYTE count* Number of sectors to write (1.255) */if (count > 1)SD-WriteMUltiBloCkS(Uint8_t *)buff, sector*BLOCK_SIZE, BLOCK_SIZE,count);* Check if the Transfer is finished */SD-WaitWriteOperation(); 等待 dma 传输结束while(SD_GetStatus() != SD-TRANSFER_0K); 等待 sdio 到 Sd 卡传输结束elseSD_WriteBlock(uint8_t *)buff,sector*BLOCK一SIZE, BLoCK一SIZE);* Check if the Transfer is finished */SD.WaitWriteOperation();等待dma传输结束while(SD_GetStatus() != SD-TRANSFER_0K); 等待 sdio 到 sd 卡传输结束return RES-OK;#endif * .READONLY 7户7* Miscellaneous Functions7DREsULTdiskJoctI (BYTE dr* Physical drive nmuber (0.) */BYTE Ctrl,* Control code */void *buff*Buffertosend/receivecontroldata*/returnRES_OK;扇区写入函数与扇区读取函数十分相似,也是根据写入扇区的数目是一个还是多个来分别调用不同的SD数据块写入函数.d.时间接口函数DWORDget-fattime(void)return0;这个函数在本系统中意义不大,所以并未添加实际功能.到这里FATFS的移植与文件系统的根本函数介绍已经完成.下面将介绍UiP及socket实现方法.6.ip及socket实现方法ENC28J60模块与STM32F103ZET6连接PB13:ENC28J60-1NTPA6-SPI1-MISO:ENC28J60-SOPA7-SPI1-MOSI:ENC28J60-SIPA5-SPI1-SCK:ENC28J60-SCKPA4-SPI1-NSS:ENC28J60-CSPEl:ENC28J60-RSTENC28J60模块电路图:ENC28J60简介ENC28J60是带有行业标准串行外设接口SeriaIPeripheraIInterface,SPI的独立以太网控制器。它可作为任何三备有SPI的控制器的以太网接口。ENC28J60符合IEEE802.3的全部标准,采用了一系列包过滤机制以对传入数据包进行限制.它还提供了一个内部DMA模块,以实现快速数据吞吐和硬件支持的IP校验和计算。与主控制器的通信通过两个中断引脚和SPI实现,数据传输速率高达10Mb/s.两个专用的引脚用于连接LED,进行网络活动状态指示。ENC28J60由七个主要功能模块组成:1 .SPI接口-充当主控制器和ENC28J60之间通信通道。2 .控制存放器用于控制和监视ENC28J60.3 .双端口RAM缓冲器用于接收和发送数据包。4 .判优器-当DMA、发送和接收模块发出请求时对RAM缓冲器的访问进行控制.5 .总线接口-对通过SPl接收的数据和命令进行解析。6 .MACMediumAccessControl模块实现符合正EE8023标准的MAC逻辑.7 .PHY物理层)模块对双绞线上的模拟数据进行编码和译码.该器件还包括其他支持模块,诸如振荡器、片内稳压器、电平变换器提供可以接受5V电压的I/O引脚)和系统控制逻辑。UIP移植步骤:Uip文件结构:先介绍下UiP下各个目录文件的功能:I-Wps叩PS目录下为uip提供的一些应用例如IIdhcpcIIhello-worldIIreSOlVIIsmtpIHtelnetdIIwebclientI1webserverII-d4s-docdoc下放置的为说明文档,程序中用不上IJhtmlHibIib下为内存块管理函数源码I-UiPuip下为uip和核心实现源码L-UniXUniX环境里的uip应用例子,可以参照这个例子实现应用Uip+stm32MDK下工程建立Uip的数据通过网卡Enc28j60从物理层剥离,所以需要先配置Uip和Enc28j60的数据交互。这个局部在tapdev.c文件中:Ol*include"uip.h,02*include,ENC28J60.h"05voidtapdevjnit(unsignedchar*my-mac)0708enc28j60Init(my_mac);0910unsignedint12tapdev_read(void)1314returnenc28j60PacketReceive(UIP一CoNF一BUFFER_SlZE,uip_buf);151617void18tapdev_send(void)1920enc28j60PacketSend(uip一len,uip一buf);21Socket是什么呢?SoCket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让SoCket去组织数据,以符合指定的协议.先从效劳器端说起.效劳器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.在这时如果有个客户端初始化一个Socket,然后连接效劳器(ConneCt),如果连接成功,这时客户端与效劳器端的连接就建立了。客户端发送数据请求,效劳器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。