×
单片机 > 单片机程序设计 > 详情

在mini2440上面搞定CC2500物理层驱动

发布时间:2020-05-26 发布时间:
|
  1. 写在前面
     

最近基本搞定了CC2500在linux下面的驱动,在这个过程中遇到了好多问题,特此总结出来和大家分享。但是需要注意的事情是:
第一,本文不负责程序的具体讲解,诸如每一行程序都讲什么:这个在程序的里面有注释。本篇文章更侧重于从整体结构上让大家对于linux下的CC2500DE驱动有一个整体的了解,如果到时候需要深入研究的时候,再来看具体的代码;
第二,很多师弟师妹没有习惯在linux下编程,或曰,在操作系统下面写程序;并且,对于本专业的东西理解并不扎实。但是,文章中不可能就每一个涉及到的概念都大加讲解。因此,这篇文章还是需要有一些基础才能来阅读的,如果遇到什么不明白的地方,请及时来问我,或者跟帖,或者自己查资料。毕竟,这个驱动花费了我半个多月的时间,其中遇到的问题,不可能在一篇文章中就讲得清清楚楚。


  1. 背景知识
     

如果大家看过实验室的WSN方面的程序,可能会被其中的架构搞得天昏地暗。这里,简单的帮助大家把实验室的WSN的软件、硬件方面捋一捋。
说到软件,就不能不说硬件。我们的节点由两部分组成,一个是单片机,一个是CC2500的射频模块。射频模块通过SPI接口和单片机进行通信。除了SPI模块用到的4条线,实际上CC2500还需要告诉单片机“我接收到一个数据”,这个地方CC2500可以通过配置GDO来实现。我们这里采用的CC2500的GDO0作为单片机的一个外部中断,当接收到数据的时候,GDO0就会变为低电平,然后触发CC2500的中断,从而接收数据。
那么,CC2500究竟是如何来进行数据发送、接收的呢?其实很简单。如果大家稍微用过一点可以控制的芯片就会知道,很多芯片内部都有寄存器,CC2500之所以能够完成发送、接收的功能,就是通过配置寄存器来完成的。比较重要的配置有信道、波特率、信号强度等等,另外,CC2500还有64bytes的RXFIFO和64bytes的TXFIFO,他们的作用就是用来发送和接收。当配置CC2500结束之后,如果用来发送,那么就把数据放在TXFIFO里面,然后把CC2500的状态变为发射状态,数据就自动发送出去了。同样的道理,当RXFIFO里面接收到了数据之后,CC2500就会如前所说,发出一个中断,通知单片机来读取数据。
单片机通过SPI接口读出了CC2500中的数据,就需要进行简单的处理了。我们的节点是遵循802.15.4协议的,如果有兴趣,大家可以去看一看这个协议的内容。对于我们来说,比较重要的就是帧的结构。下面这个是物理层帧的结构:
字节:4
1
1
可变
先导码
帧定界符

帧长度(7位)

保留位(1位)
负载内容

同步头

PHY头

PHY负载

简单的讲,因为是无线通信,没有一个时钟的存在,因此本质上所有的无线通信都是一个异步的通信。那么,就需要发送和接收的双方首先对数据进行同步——这个就是同步头的作用。随后的是PHY的头,很简单,就是一个长度。换句话说,每个帧不能够超过2的8次方减一个数据(127个)。随后就是负载的内容了,简单吧?另外,由于CC2500是一个802.15.4兼容的芯片,这一部分内容都已经固化在芯片里面了,像同步头这种东西,都是芯片帮我们完成(当然也可以自行配置)。我们只需要把写入的数据大小和数据内容写入即可。
虽然简单,但是还有一个问题:每次从CC2500中读取了数据,存放到哪里呢?有的同学可能想的比较简单,比如建立一个大数组,收到多少个数,放在里面就行了。但是,这样就会遇到问题:如果收到了两个包,上层来不及处理怎么办?建立两个大数组?更多的包呢?当然,我们可以通过建立好多个数组来解决这个问题。但是,从效率上讲,这个是非常笨的一个办法。因此,我们建立了一个环形缓冲区PHY_RX_BUFFER,来解决这个问题。环形缓冲区的具体原理不过多涉及,可以自行百度。
另外一个问题就是,如果我们从CC2500中接收到了数据,那么应该怎样才能告诉上层来进行处理呢?这里就要用到操作系统的消息机制了。如果确定接收到了一个数据,那么就给上层发送一个消息,来通知上层。上层接收到这个消息,就会知道接收到了数据。
当从CC2500读取到了数据,因为物理层没有什么东西,我们就直接通知MAC层来处理数据。MAC层的结构如下:
字节:2
1
0/2
0/2/8
0/2
0/2/8
可变
2
帧控制域
序列号
目的PAN标示符
目的地址
源PAN标示符
源地址
帧负载
FCS
地址域
 
 
MAC帧头
MAC负载
MFR

具体内容可以参见老师的《无线传感器网络技术与工程应用》这本书,这里不再详细的讲,大概内容就是,会有一系列的帧头,然后是帧数据,最后有校验。而我们最关心的就是帧的内容。但是,帧头也比较麻烦,因此我们需要首先对帧头进行处理,把数据给“剥离”出来,这也是基本上所有的协议栈解析协议的方法。


  1. 硬件结构
     

硬件上,我们采用的是mini2440开发板,和实验室的CC2500节点(把单片机焊掉,然后把mini2440的相应管脚接到上面)。


Mini2440方面,我们用的是CON8这个引脚,具体连线如下:
EINT8对应S3C2440的GPG0引脚,连接到了CC2500的GDO2
EINT11对应S3C2440的GPG3引脚,连接到了CC2500的CSN
EINT13对应S3C2440的GPG5引脚,连接到了CC2500的SO
EINT14对应S3C2440的GPG6引脚,连接到了CC2500的SI
EINT15对应S3C2440的GPG7引脚,连接到了CC2500的CLK
EINT19对应S3C2440的GPG11引脚,连接到了CC2500的GDO0
除此之外,还有3.3V电源和GND


  1. Linux下驱动编写
     

为什么要用驱动呢?简单的说,就是为了在linux下完成用户空间和内核空间的交互。本质上,驱动就是完成了两件事情:第一,初始化SPI接口,并且通过SPI和CC2500完成通信。第二,完成读写函数,read和write,通过读写函数的接口来对CC2500进行操作。
附件里面有几个文件,这里简单讲解一下:
Common.h主要是包含了很多linux的头文件,并且对一些数据类型做了定义
CC2500.h里面主要是对CC2500的寄存器做了一些定义,没啥东西
Driver.h主要是定义了很多和CC2500进行通信,并且利用CC2500进行发送和接收的内容
Phy.h主要是完成了一些诸如信道设定、通信速率等功能,并且把CC2500里的数据读到环形缓冲区里面
CC2500.c这个就是完成设备注册、读写函数、iocontrol等功能
这样有了一个整体的把握之后,不用看很多代码,学会使用就好。


  1. 驱动接口使用
     

那么,这个驱动怎样使用呢?主要是通过read/write/ioctl三个接口来实现的。
在应用程序中read/dev/CC2500这个文件,如果有数据,会返回相应的数据。其中,返回的数据中第一个字节为数据的长度(不包括该字节本身),后面为相应的数据。
如果是写文件的话,只需要向/dev/CC2500中写入相应的数据即可。写入的时候需要注意,第一个字节同样为数据长度(不包括该字节本身),后面跟相应的数据。当写入的字节合适的时候,驱动会自动执行发送函数。如果写入的字节数量和写入的数据第一个字节不匹配的时候,会返回错误。
ioctl主要有这样几种命令:


CC2500_IOC_PHY_DETECT_STATUS
表示将CC2500的状态变为RX
CC2500_IOC_PHY_GET_BAUDRATE
读取CC2500的波特率,返回的是1,2,3,4,分别是2.4k,10k,250k,500k的波特率
CC2500_IOC_PHY_SET_BAUDRATE
设定CC2500的波特率,后面的参数是1,2,3,4,分别是2.4k,10k,250k,500k的波特率
CC2500_IOC_PHY_GET_TXPOWER
获取发送功率,返回的是在发送功率表{0x00, 0x50, 0x44, 0xC0, 0x84, 0x81, 0x46, 0x93, 0x55, 0x8D, 0xC6,0x97, 0x6E, 0x7F, 0xA9, 0xBB, 0xFE, 0xFF }中的数组下标
CC2500_IOC_PHY_SET_TXPOWER
设定发送功率,参数的是在发送功率表{0x00, 0x50, 0x44, 0xC0, 0x84, 0x81, 0x46, 0x93, 0x55, 0x8D, 0xC6,0x97, 0x6E, 0x7F, 0xA9, 0xBB, 0xFE, 0xFF }中的数组下标
CC2500_IOC_PHY_GET_CHANNEL
获取频段,参数是频段表{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0,0xB0, 0xC0, 0xD0, 0xE0, 0xF0 }中的数组下标
CC2500_IOC_PHY_SET_CHANNEL
设定频段,参数是频段表{0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0,0xB0, 0xC0, 0xD0, 0xE0, 0xF0 }中的数组下标


  1. 应用层程序编写
     

这里的应用层主要是将mac层实现了。其实,mac层的作用也很简单,主要是对要发送的包进行封装,接收回来的包进行包的解析。这一部分的内容还不全面,仅仅是将原来的程序移植过来,还有很多需要改动的地方,有待补充。因此,这里只是提供源代码,具体的说明请参见程序内部


  1. 如何配置内核文件
     

1首先修改/opt/FriendlyARM/mini2440/linux-2.6.32.2/drivers/char/Kconfig
加入如下几句话:
config CC2500_DRIVER


        tristate "CC2500 driverfor FriendlyARM Mini2440 development boards"


        depends on MACH_MINI2440


        default m if MACH_MINI2440


        help


        this is CC2500 driver forFriendlyARM Mini2440 development boards




2 修改Makefile,让CC2500的驱动可以编译。
加入如下几句话:


obj-$(CONFIG_CC2500_DRIVER)     +=CC2500inuse.o


CC2500inuse-objs :=./CC2500.in_use/cc2500.o ./CC2500.in_use/phy.o./CC2500.in_use/driver.o


其中,CONFIG_CC2500_DRIVER 需要和Kconfig中的configCC2500_DRIVER
保持一致。
CC2500inuse-objs这句话,意思是CC2500inuse.ko是由哪几个文件组成的。这样就实现了模块化的处理。
需要注意的是,CC2500inuse-objs和冒号之间需要有个空格,不然会出错误。这个地方我搞了好久。
具体的Makefile的格式也可以参考这两篇文章:
http://blog.csdn.net/tommy_wxie/article/details/7282463
http://hi.baidu.com/wjq_qust/blog/item/97ddbdfdfb2e541309244d30.html


  1. 调试过程中的一些问题
     

CC2500驱动调试的过程中,遇到的一些问题
第一,在调试的过程中,发现板子和2500没有办法进行通信。一开始怀疑是2500芯片的问题,后来确认2500芯片没有问题之后,用示波器看了一下波形,发现SPI引脚上没有输出。进而发现,对SPI寄存器进行读写的时候,无论写入是什么,读出的都是0。后来发现,原来是PCLK(也就是SPI模块的时钟)没有使能,囧。设置了寄存器之后,问题就解决了。
第二,CC2500驱动没有办法自动在/dev/目录下面创建节点。这个问题可以参考下面几篇文章:
http://blog.csdn.net/cjok376240497/article/details/6848536
整体来说,创建节点的工作是这样的:
我们通过udev来创建节点。但是,udev是应用层的东东,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,只要在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用class_device_create创建对应的设备。
换句话说,首先要为每一个设备创建一个类。然后用这个类去申请一个设备节点。
第三,在调试的过程中,发现只要一启动CC2500,那么液晶屏就熄灭了。调试了半天也没有找到原因。
最后发现,在配置S3C2440中GPGCON寄存器的时候,对寄存器直接进行了覆盖性的修改;而该寄存器还管理着LCD的电源...于是,对寄存器进行覆盖性修改的时候,悲剧了...
总结一下吧,在linux下2500驱动编写的过程中,如果涉及到寄存器级别的操作,和单片机一样,必须十分注意各个模块之间的关系,以及相关模块的寄存器配置,不要认为linux会全部给你配好!
关键字:mini2440  CC2500  物理层驱动 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Keil(MDK-ARM)系列教程(七)_菜单