嵌入式开发中的C语言技巧你知道多少?
想学习单片机的同学可以关注、私信我或者在评论区回复我要入门。这一期我们来分享一下在嵌入式开发中常用的C语言技巧。看完这一篇文章,至少可以让你的C语言向前迈进一大步。
1.指向函数的指针
指针不单单是指向变量、字符串、数组,它还能够指向函数。在C语言中允许将函数的入口地址赋值给指针。这样就可以通过指针对函数进行访问。还可以把函数指针当成参数来传递。函数指针可以简化代码,减少修改代码的工作量。接下来我们通过一些具体的例子来了解一下。
/*函数指针简单讲解
*通过指向函数的指
*针调用比较两个数
*大小的程序
*/
#include
using namespace std;
/*比较函数声明*/
int max(int,int);
/*指向函数的指针声明(此刻指针未指向任何一个函数)*/
int (*test)(int,int);
int
main(int argc,char* argv[])
{
int largernumber;
/*将max函数的入口地址赋值给
*函数指针test
*/
test=max;
/*通过指针test调用函数max实
*现比较大小
*/
largernumber=(*test)(1,2);
cout<<><>
return 0;
}
int
max(int a,int b)
{
return (a>b?a:b);
}
通过上面这部分大家可以了解到函数指针其实和变量指针、字符串指针是大同小异的。如果大家理解了这个小程序,那么理解起下面这个有关Nand flash的源代码就会容易一些。
typedef struct {
void (*nand_reset)(void);
void (*wait_idle)(void);
void (*nand_select_chip)(void);
void (*nand_deselect_chip)(void);
void (*write_cmd)(int cmd);
void (*write_addr)(unsigned int addr);
unsigned char (*read_data)(void);
}t_nand_chip;
static t_nand_chip nand_chip;
/* NAND Flash操作的总入口,它们将调用S3C2410或S3C2440的相应函数 */
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_data(void);
/* S3C2410的NAND Flash处理函数 */
static void s3c2410_nand_reset(void);
static void s3c2410_wait_idle(void);
static void s3c2410_nand_select_chip(void);
static void s3c2410_nand_deselect_chip(void);
static void s3c2410_write_cmd(int cmd);
static void s3c2410_write_addr(unsigned int addr);
static unsigned char s3c2410_read_data();
/* S3C2440的NAND Flash处理函数 */
static void s3c2440_nand_reset(void);
static void s3c2440_wait_idle(void);
static void s3c2440_nand_select_chip(void);
static void s3c2440_nand_deselect_chip(void);
static void s3c2440_write_cmd(int cmd);
static void s3c2440_write_addr(unsigned int addr);
static unsigned char s3c2440_read_data(void);
/*初始化NAND Flash */
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/*判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
nand_chip.nand_reset = s3c2410_nand_reset;
nand_chip.wait_idle = s3c2410_wait_idle;
nand_chip.nand_select_chip = s3c2410_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;
nand_chip.write_cmd = s3c2410_write_cmd;
nand_chip.write_addr = s3c2410_write_addr;
nand_chip.read_data = s3c2410_read_data;
/*使能NAND Flash控制器,初始化ECC,禁止片选,设置时序 */
s3c2410nand->NFCONF = (1<
}
else
{
nand_chip.nand_reset = s3c2440_nand_reset;
nand_chip.wait_idle = s3c2440_wait_idle;
nand_chip.nand_select_chip = s3c2440_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
nand_chip.write_cmd = s3c2440_write_cmd;
#ifdef LARGER_NAND_PAGE
nand_chip.write_addr = s3c2440_write_addr_lp;
#else
nand_chip.write_addr = s3c2440_write_addr;
#endif
nand_chip.read_data = s3c2440_read_data;
/*设置时序 */
s3c2440nand->NFCONF = (TACLS<
/*使能NAND Flash控制器,初始化ECC,禁止片选 */
s3c2440nand->NFCONT = (1<
}
/*复位NAND Flash */
nand_reset();
}
这段代码是用于操作Nand Flash的一段源代码。首先我们看到开始定义了一个结构体,里面放置的全是函数指针。他们等待被赋值。然后是定义了一个这种结构体的变量nand_chip。然后是即将操作的函数声明。这些函数将会被其他文件的函数调用。因为在这些函数里一般都只有一条语句,就是调用结构体的函数指针。接着往下看,是针对两种架构的函数声明。然后在nand_init函数中对nand_chip进行赋值,这也就是我们刚刚讲过的,将函数的入口地址赋值给指针。
现在nand_chip已经被赋值了。如果我们要对Nand进行读写操作,我们只需调用nand_chip.read_data()或者nand_chip.write_cmd()等等函数。这是比较方便的地方,另外,这个代码的移植性很强,如果我们又用到了一种芯片,我们就不需要改变整篇代码,只需在nand_init函数中增加对新的芯片的判断,然后给nand_chip赋值即可。所以我说函数指针会使代码具有可移植性,易修改性。
2.C语言操作寄存器
在嵌入式开发中,经常要使用寄存器,对寄存器进行写入,读出等等操作。每个寄存器都有自己固有的地址,通过C语言访问这些地址是一个很重要的环节。
#define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
在这里,我们举一个例子。这是一个状态寄存器的宏定义。首先,通过unsigned int我们能够知道,该寄存器是32位的。因为要防止程序执行过程中直接从cache中读取数据,所以用volatile进行修饰。每次都要重新读取该地址上的值。首先(volatile unsigned int*)是一个指针,我们就假设它为p吧。它存储的地址就是后面的0x560000B0,然后取这个地址的值,也就是*p,所以源代码变成了(*(volatile unsigned int *)0x560000B0),接下来我们就能直接赋值给GSTATUS1来改变地址0x560000B0上存储的值了。
/* NAND FLASH (see S3C2410 manual chapter 6) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFECC;
} S3C2410_NAND;
static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT;
有的时候,我们会看到类似这样一种情况的赋值。其实这和我们前面说的差不多。只不过这里是在定义了指针的同时对指针进行赋值。这里首先定义了结构体S3C2410_NAND,里面全部是32位的变量。又定义了这种结构体类型的指针,且指向0x4e000000这个地址,也就是此刻s3c2410nand指向了一个实际存在的物理地址。s3c2410nand指针访问了NFSTAT变量,但我们要的是它的地址,而不是它地址上的值。所以用&取NFSTAT地址,这样再强制转换为unsigned char型的指针,赋给p,就可以直接通过p来给NFSTAT赋值了。
本期先分享到这里,想要进群学习单片机编程的同学可以私信我,回复“我要入门”,与我们一起成长,喜欢的可以点个赞关注我们!
农田环境监测系统设计与实现
摘 要 : 针对农业生产的自动化管理需求,介绍了ARM与ZigBee技术相结合设计农田环境监测系统的方法。硬件部分采用ZigBee外接传感器设计环境数据采集节点,采用S3C2440处理器和CC2530设计协调器网关,其中CC2530与ARM之间采用SPI口进行数据通信。软件部分基于IAR平台结合Z-Stack协议栈构建ZigBee无线监测网络,用于采集并传输影响农作物生长的现场环境信息,数据在基于ARM与ZigBee技术相结合而开发的网关节点处实现汇聚,最终通过串口发送到基于VC++6.0与数据库技术开发的上位机监测系统。经测试,网关节点能实时接收传感器节点的数据,并通过串口成功上传到上位机监测系统,实现实时数据监测、动态分析、历史查询和异常报警功能,系统具有较好的稳定性和实用性。
0 引言
随着传统农业向现代化农业的转变,农业生产中降低成本、提高质量和产量、发展优质高效农业的要求以及资源高效利用、农业可持续发展等方面的要求,迫切需求社会效益、经济效益、环境保护和科学技术相结合的新型农业的出现。现代化农业基于精细农业理念,20世纪80年代美国提出精细农业构想,采用精细农业可以在保护环境、降低成本的前提下实时监测农田环境多元信息,及时进行科学分析与预测,根据农田环境信息变化来确定最合适的管理决策,帮助农民科学种植。
目前,关于基于嵌入式技术农业应用监测系统的研究还较少,大多采用单片机和便携式设备实现环境信息采集[1-2]。针对现代农业自动化管理的需求,本文采用S3C2440处理器结合ZigBee技术设计了一个农田环境监测系统[3]。ZigBee这种低功耗、低成本、近距离、低复杂度的双向无线传感网络技术是进行大面积农田环境信息采集与传输的最佳选择。以PC为上位机,采用VC++开发工具与数据库技术[4]开发上位机监测软件,采用串口通信技术连接协调器网关,构成分布式信息采集系统。该系统实现了对空气温度、空气湿度、光照强度、土壤水分等环境信息的实时数据采集、传输、存储、查询及分析处理,并且具有异常预警功能,有利于降低农业作业的成本,提高效率,同时也为农业工作者科学种植、防治病虫灾害提供可靠、充分的科学依据。
1 系统总体设计
农田环境监测系统,主要包括数据采集终端、数据通信链路和数据监测中心三部分。其中数据采集终端包括采集农作物生长环境的各种传感器,如空气温湿度传感器、光照强度传感器、土壤水分传感器;数据通信链路包括ZigBee近距离无线通信网络、ZigBee与ARM间的SPI数据通信和ARM与PC间的串口通信;数据监测中心包括安装了单机版监测软件的PC及PC内部运行的MySQL数据库。系统结构框图如图1所示。
ZigBee终端节点传感器能够采集空气温度、空气湿度、光照强度、土壤水分等环境因素,然后经过ZigBee无线网络将数据发送到ZigBee协调器节点,协调器节点数据由SPI总线传输到ARM处理器,数据在ARM处理器中经过分析处理之后通过串口最终发送到PC数据库服务器中存储[5-6]。系统可对影响农作物生长的环境信息进行实时远程监测、动态分析和历史查询,并且对超出标准值的环境参数提供报警功能。
2 系统硬件设计
2.1 传感器节点
传感器节点由ZigBee无线通信模块(包括8051处理器)、传感器模块和电源模块组成。传感器模块采集和转换监测区域内的环境信息;无线通信模块用于控制传感器节点的数据采集、存储、转换和处理、收发数据及与其他传感器节点间交换信息等;电源模块负责为传感器节点运行提供所需的能量。传感器节点结构框图如图2所示。传感器模块包括空气温湿度传感器DHT11、光照强度传感器MAX44009和土壤水分传感器TDR-3。ZigBee无线通信模块节点选用WeBee的CC2530+CC2591(PA)模块。CC2530是一个低成本、低功耗的无线微控制器,芯片上集成了CC2591射频前端放大芯片、增强型8051内核、Flash存储器、8 KB RAM和许多其他增大功能。CC2530+CC2591(PA)模块具有以下功能特点:
(1)可靠传输距离400 m,自动重连距离高达360 m;
(2)工作在2.4 GHz频段,无线速率达250 kb/s;
(3)具有16个通信信道,可根据环境切换可靠通信信道。
2.2 协调器网关
考虑到协调器节点需要处理传感器节点上传的大量数据,并能够将数据通过串口有序地上传到上位机,要求协调器节点具有较快运行速度和较强的数据处理能力,为此选用S3C2440A开发板和ZigBee CC2530无线收发模块设计协调器网关节点。ZigBee模块数据处理程序工作在协调器模式下,与处理器S3C2440之间通过SPI口进行数据通信。协调器网关硬件框图如图3所示。基于ARM920T内核的S3C2440A具有MMU单元和丰富的外部接口,主要片上功能特点如下:
(1)主频(FCLK):400 MHz;
(2)存储器:2 MB NOR Flash,256 MB NAND Flash,64 MB SDRAM;
(3)外部存储控制器:可扩展8组128 MB的存储空间,总容量达1 GB;
(4)具有2路SPI口,3路URAT口;
(5)CMOS摄像头接口。
3 系统软件设计
3.1 协调器节点程序
采用IAR Embedded Workbench(7.60A)编程环境和TI的ZStack-CC2430-2.3.0-1.4.0(ZigBee 2007)协议栈设计ZigBee CC2530无线传感网络程序[7-9]。协调器节点负责启动和配置网络,同时可以与网络以外的设备进行通信,起到网关的作用。根据设计目标,调用ZigBee协议栈中的API接口函数来实现无线传感网络程序设计。协调器的程序设计流程图如图4所示。程序设计方法如下:
(1)初始化硬件、网络层、任务、堆栈等。编写init_spi函数用来初始化SPI口,协调器组网程序初始化通过调用协议栈中的初始化函数来实现,其中osal_init_system函数用来初始化操作系统,osalInitTasks函数用来初始化任务。
(2)信道扫描,根据扫描结果选择一个编号在11~26间合适的信道,并设置网络PAN ID(0x0000-0x3FFF),协调器采用的网络PAN ID默认为0x0000。接着启动协调器,等待其他设备加入网络。
(3)在有传感器节点加入网络之后,调用接收函数。当协调器收到从传感器节点传来的数据后,将数据发送到S3C2440处理器。
3.2 传感器节点程序
传感器节点负责采集环境参数,并将其传送到协调节点。传感器节点分为终端节点和路由节点两种,终端节点仅用于采集传感器数据并发送给附近的路由器或协调器;路由器节点同时负责数据采集和数据信息的转发。本文设计的路由节点程序和终端节点的程序除了有些配置参数不同,其他部分基本一致。传感器节点在上电后,Z-Stack调用ZDO_StartDevice函数来启动设备进行初始化,接着会读取非易失性存储器中的一系列配置参数,如设备类型参数ZCD_NV_LOGICAL_TYPE,默认的扫描信道参数ZCD_NV_CHANLIST等。其中设备的类型参数值决定着此设备的逻辑类型。本文传感器节点程序中终端设备的类型参数值为ZG_DEVICETYPE_ENDDEVICE,路由器的类型参数值为ZG_DEVICETYPE_ROUTER。
传感器节点的程序设计流程图如图5所示。以DHT11数字温湿度传感器为例,传感器节点程序设计主要步骤如下:
(1)设计DHT11的裸机驱动程序,使CC2530可以驱动DHT11传感器。包括配置传感器IO口方向、接收缓存定义、延时、温湿度写入、传感启动、设置采集周期等。
(2)将DHT11的裸机程序文件复制到协议栈Source文件夹下,在协议栈的APP目录下点击右键——Add——添加复制文件,这样DHT11的裸机驱动程序被添加到Z-stack协议栈代码中。在协议栈代码中初始化传感器引脚P0.6,通过周期性广播函数每2 s读取温度传感器1次。
(3)调用API函数初始化硬件、网络层、堆栈等,在发送函数中将数据打包并按指定方式发送给指定设备。
3.3 ARM与ZigBee间SPI驱动程序设计
在ARM上移植Linux操作系统,要在Linux系统用户空间使用SPI设备,首先应在内核空间移植相应的设备驱动程序,接着在用户空间编写ARM端数据收发程序。在Linux环境下,设备被当做文件进行处理,对设备的操作都是通过操作文件系统来实现的[10]。ARM与CC2530间的SPI通信程序主要组成部分如下:
(1)在内核中注册SPI设备,初始化驱动设备,使相关寄存器映射到虚拟内存地址。设备注册、建立设备文件、映射GPIO与SPI寄存器、注册中断均由spi_init函数实现。
(2)修改file_operations文件结构。file_operations文件结构用来对系统内部I/O设备存取入口点向系统进行说明。这样应用程序中通过read、write等关键字即可调用驱动程序相应的函数。
(3)SPI调用spi_open打开设备,调用spi2440_write发送数据,调用spi2440_read接收数据,调用spi_close函数关闭设备。
(4)设备IO控制。spi_ioctl函数负责接收应用程序参数,并对CC2530片选或复位操作。
(5)interrup_irq中断函数实现。
4 系统测试
在实验室使用4个CC2530开发板,1个作为协调器,1个作为路由器,2个作为终端节点对系统可行性及稳定性进行了测试。分别选择CoordinatroEB-Pro、RouterEB-Pro、EndDeviceEB-Pro模式下载程序到这三类节点开发板。系统ZigBee终端和路由节点安装了DHT11数字型空气温湿度传感器,设置采集时间间隔为20 min,上位机监测系统实时环境数据测试如图6所示。设置动态分析时间间隔为10 min,通过上位机监测系统可查看各传感器上传的环境信息动态分析图如图7所示。
5 结论
本文分析了农业环境发展现状与应用需求,针对当前农田环境监测存在的不足,采用ARM结合ZigBee无线传感技术设计了农田环境监测系统。该系统同时具备了ZigBee技术的成本低、功耗小、网络结构简单、自组织传输和ARM嵌入式技术对海量数据信息的高速处理的特点。系统可实现对农田环境信息有效采集、融合、传输、存储、查询与分析处理的一体化管理。
参考文献
[1] 张帆,刘刚.基于.NET的农业生产环境信息监测系统[J].计算机工程与设计,2013,34(2):696-701.
[2] 薛文英,傅平,张馨,等.基于组态平台的日光温室群监控系统软件设计与应用[J].北方园艺,2011(9):53-56.
[3] 于海业,罗瀚,任顺,等.ZigBee技术在精准农业上的应用进展与展望[J].农机化研究,2012,8(8):66-68.
[4] 张永梅.MySQL数据库技术在公民健康信息管理系统中的应用[D].西安:西安电子科技大学,2010.
[5] 王正强.VC中应用MSComm控件实现串口通信[J].电子测试,2010,5(5):73-76.
[6] FORTA B. MySQL必知必会[M].刘晓霞,钟鸣,译.北京:人民邮电出版社,2009.
[7] ZigBee Alliance. ZigBee Specification Version 1.0[M]. ZigBee Standards Organization, 2004.
[8] Zhang Jie, Li Aicheng, Li Jianlong, et al. Research of real-time image acquisition system based on ARM 7 for agricultural environmental monitoring[J]. IEEE Transactions on PE, 2011:6216-6220.
[9] Yang Shuhui, Dai Fei, CARDEI M, et al. On connected multiple point coverage in wireless sensor networks[J]. International Journal of Wireless Information Networks, 2006,13(4):289-301.
[10] 王舒憬,黄河,党彦博,等.基于ARM的无线ZigBee收发器驱动设计[J].仪表技术,2008(2):35-37.
相关问答
英语翻译1:我需要想出一个好主意2:我们必须在英语课堂上写下...有没有悬赏分并不重要,重要的是能解决您的问题.非常感谢您能提供给我这样的机会,在回答您的问题的同时,也提高了自身对知识的运用能力.您需要的答案...