CC1100e通过SPI方式与MSP430进行通信的。首先需要明白读写寄存器的过程:写寄存器时,先写寄存器的地址,然后写数据,这样数据就到了所要写的寄存器当中了。同样,读寄存器时,先读寄存器的地址,由于同步通讯,相应寄存器中的数据在读寄存器地址的下个周期就自动读出。
SPI接口上进行所有事务均以一个报头字节作为开始:BIT7~BIT0,在SPI总线上传输数据期间,CSN引脚必须保持低电平。
寄存器的配置:
CC1100 寄存器地址是0~0x3F,也就是BIT0~BIT5
CC1100 读写控制是BIT7,BIT7 为1 时,为读对应的寄存器,BIT7 为0 时,为写相应的寄存器。
BIT6 是突发访问控制位,BIT6 为1 为突发访问,BIT6为0 为单字节访问。
其中,SPI地址为0x00~0x2E之间的为配置寄存器,对于配置寄存器均能进行读取和写入操作。读取还是写入操作由上述的BIT7控制。写入寄存器时,每当一个报头字节或数据字节通过MOSI引脚发送时 状态字节便会通过MISO 引脚完成发送。当读取寄存器时,每当一个报头字节通过MOSI引脚发送时状态字节便会通过MISO引脚完成发送。通过设置报头字节的突发位BIT6可以高效的存取带连续地址的寄存器。地址位BIT5~BIT0在内部地址计数器内设置起始地址,每增加一个新的字节(8个时钟脉冲),计数器便增加1.突发存取可以是一次读取也可以是一次写入,但必须通过设置CSN为高电平来进行终止。
例如:配置IOCFG2寄存器,其地址0x00,我们突发访问写的话写先数据0x40,再连续写数据就可以了;如果单字节读这个寄存器,先写数据0x80,再读一下就可读出其值了。
命令滤波(指令选通脉冲):
通过寻址一条指令选通脉冲寄存器,启动内部序列。即cc1100 只要写一下对应的寄存器的地址,不用写数据,它就内部自动执行相应的指令,比如重启芯片,设置为发送模式等等,共有14 个滤波指令,地址从0x30~0x3D。
而滤波指令的状态寄存器是可读不可写的,也就是0x30~0x3D 的地址加上0xC0,比如写数据0xF4,就可以读到相应RSSI (0x34)状态寄存器里面的值。
状态字节:
状态字节包含一些关键的状态信号,当通过SPI接口发送报头字节、数据字节或指令选通脉冲时,CC1100e的状态字节通过MISO引脚发送。状态字节的最后四位(3:0)FIFO_BYTES_AVAILABLE,进行读取操作时,报头字节的BIT7设置为1,FIFO_BYTES_AVAILABLE域包含了可从FIFO读取的字节数。进行写操作时,FIFO_BYTES_AVAILABLE字段包含了可写至TX FIFO的字节数。当FIFO_BYTES_AVAILABLE=15时,表示15或更多的字节为可用字节。
FIFO存取:
关于FIFO的访问,其分为TX FIFO和RX FIFO。64字节的TX FIFO和64字节的RX FIFO均通过0x3F地址进行存取。对其既可以通过单字节访问也可以通过突发访问。当读写控制位BIT7为1时访问的是RX FIFO,BIT7是0时访问的TX FIFO。这样得到下列报头字节:0x3F为单字节访问TX FIFO,0xBF为单字节访问RX FIFO,0x7F为突发访问TX FIFO,0xFF为突发访问 RX FIFO。
当对TX FIFO进行写入操作时,每个数据字节的状态字节均在MISO上输出。该状态字节用于检测TX FIFO写数据时的下溢。状态字节包含将其写入TX FIFO过程以前自由的字节数,当可写入TX FIFO的最后一个字节在MOSI上发送时,与此同时在MISO引脚上接收到的状态字节将表明TXFIFO中存在一个自由字节。TX FIFO可能会由于写入一条SFTX指令选通脉冲而被刷新,同样,一条SFRX指令选通脉冲也会刷新RX FIFO。
PTABLE存取:
0x3E地址用于存取PATABLE,用来设置发射功率的。接收到此地址之后,SPI要求有高达8个数据字节。PATABLE是一个8字节表,其定义了PA控制设置。读写还是通过读写控制位BIT7控制,突发访问还是单字节访问还是通过突发位BIT6来控制。内部有个计数器用来控制对该表的存取,每读取或写入该表中一个字节,计数器就自动加1,当计数到7时会自动从0开始计数.当设置Csn为高电平时,内部计数器会变为0.如果向PTABLE写入一个字节,并且要读取该值,那么在读取存取以前必须将CSN设置为高电平,以将计数器重新设置为0.
数据包格式:
由cc1100的datasheet可以知道,其数据包是由前导码、同步字节、可选的数据包长度、可选的目标地址、数据区以及两个字节的CRC校验码。前导码的最小长度可以通过MDMCFG1.NUM_PREAMBLE的值进行编程。当开启TX模式时,调制器将开始发送前导码。当编程的前导字节数被发送完毕后,调制器将开始发送同步字节,然后发送来自TX FIFO的数据。若TXFIFO为空,调制器将继续发送前导码,知道第一个字节被写入TX FIFO为止,调制器随后发送同步字,然后发送数据字节。
同步字是设置于SYNC1和SYNC0两个寄存器中的2字节值。在可变数据包长度模式下,通过同步字后面的第一个字节来配置数据包长度。数据包长度被定义为有效负载数据,但不包括长度字节和可选CRC。PKTLEN寄存器用于设置RX模式中允许的最大数据包长度。任何长度字节值大于PKTLEN的接收数据包将被丢弃。
接收模式下的数据包滤波:
CC1100e支持三种不同类型的滤波:地址滤波、最大长度滤波和CRC滤波。
地址滤波:设置PKTCTRL1.ADR_CHK为0以外的任何值均可开启数据包的地址滤波器。radio将目标地址字节与ADDR寄存器中地址,以及PKTCTRL1.ADR_CHK=10时的0x00广播地址或者PKTCTRL1.ADR_CHK=11时的0x00和0xff广播地址进行比较。如果接收的地址匹配一个有效地址,则接收该数据包,并将其写入RX FIFO,如果地址匹配失败,则丢弃该数据包,并重新启动接收模式。
最大长度滤波:在可变数据包长度模式下,PKTLEN.PACKET_LENGTH寄存器的值用来设置最大允许数据包长度,当接收字节值比这个值大,则数据包被丢弃,并且重新启动接收模式。
CRC滤波:(不懂,不写)
发送模式下的数据包处理:
必须要将即将发送的有效数据长度写入TX FIFO中,开启可变数据包长度以后,长度字节必须最先被写入。长度字节具有一个与数据包有效负载相当的值。如果接收机端开启了地址识别,则写入TX FIFO的第二个字节必须为地址字节。如果开启了固定数据包长度,则写入TX FIFO的第一个字节应为地址字节。
调制器会首先发送编程的前导字节数,如果TX FIFO中的数据可用,则调制器会发送两个字节同步字,之后是TX FIFO 中的有效负载。如果开启了CRC,则在所有取自TXFIFO的数据上计算验和,并在有效负载之后以两个额外字节发送该结果。如果TXFIFO在发送完全部数据包以前变为空,那么该无线电设备将进入TXFIFO_UNDERFLOW状态。退出该状态的唯一方法是发出一个SFTX选通脉冲。
接收模式下的数据包处理:
在接收模式下,解调器和数据包处理器将会搜索一个有效的前导和同步字。如果找到,解调器就获得了位和字节同步机制,并将接收第一个有效负载字节。当可变数据包长度模式开启时,则第一个字节为长度字节。数据包处理器把这个值作为数据包长度存储,并接收该长度字节线束数目的字节。
固件中的数据包处理:
在固件中执行数据包导向无线协议时,MSP430需要知道一个数据包何时被接收到、何时被发送出去。当数据包长度大于64字节时,需要在RX模式下读取RX FIFO,需要在TX模式下重填TXFIFO。
中断驱动法:当通过设置IOCFGx.GDOx_CFG=0x06接收到或发送出去一个同步字或接收到发送出去一个完整的数据包时,在RX和TX模式下均可使用GDO引脚来实现中断。
SPI轮询:可以某个给定速率对PKTSTATUS寄存器轮询,以获取GDO2和GDO0当前值的相关信息。可以某个给定速率对RXBYTES和TXBYTES寄存器轮询,以获取RXFIFO和TXFIFO中所含字节数的相关信息。另外,在SPI总线上每发送一个报头字节、数据字节或指令选通脉冲时,可从MISO线路上返回的芯片状态字节读取到RXFIFO和TXFIFO中所含的字节数。
具体编程时必须注意一下事项:
1、拉低CSN引脚电平时,在开始传输报头字节以前,msp430必须等待SISO引脚变为低电平为止,表明内部稳定。
2、每当一个字节通过SI引脚写入到寄存器时,状态字节将被送到MISO引脚
3、状态字节的最后4位用来表示FIFO的可用字节,其最大值是15,此时表示15或更多的字节是可以使用的。
4、只有radio处于XOSC空闲,并且数字中心的能量开启,其他模块处于功率降低状态,这时候频率和信道配置才能被更新。(不是很理解)
5、寄存器的突发访问时,内部计数器会自动设置起始地址,每增加一个字节,地址会自动加1,无论读写操作,必须通过拉高Csn终止操作。
6、关于命令滤波,其实是单字节指令,通过指令对寄存器的选址,内部功能做出相应的启动或关闭。不像对其他寄存器一样必须先写地址后写数据。
7、当radio进入休眠状态时,两个FIFO都被刷新为空。
8、一般所有的滤波命令会立即执行,只有休眠滤波命令SPWD不会立即执行,它会延迟到Csn为高电平时执行。
部分程序如下:
//SPI读取和发送函数
unsigned int SpiTxRxByte(unsigned data)
{
unsigned char i,temp;
temp=0;
SCK_0;
for(i=0;i<8;i++)
{
if(data&0x80)
{
MOSI_1;
}
else
{
MOSI_0;
}
data<<=1;
SCK_1;
temp<<=1;
if(MISO)temp++;
SCK_0;
}
teturn temp;
}
//重启芯片函数
void RESET_CC1100(void)
{
CSN_0;
while(MISO);
SpiTxRxByte(SRES);
while(MISO);
CSN_1;
}
//上电重启芯片函数
void POWER_UP_RESET_CC1100(void)
{
CSN_1;
delayus(1);
CSN_0;
delayus(1);
CSN_1;
delayus(41);
ESET_CC1100();
}
//单字节写寄存器函数
void SpiWriteReg(unsigned char addr,unchar int value)
{
CSN_0;
while(MISO);
SpiTxRxByte(addr); //写入地址
SpiTxRxByte(value);//写入配置
CSN_1;
}
//突发访问方式写寄存器函数
void SpiWriteBurstReg(unsigned int addr,unsigned int *buffer,unsigned int count)
{
unsigned int i,temp;
temp=addr|0x40;//将突发访问位置1
CSN_0;
while(MISO);
SpiTxRxByte(temp);
for(i=0;i<count;i++)
{
SpiTxRxByte(buffer[i]);
}
CSN_1;
}
//写命令滤波函数
void SpiStrobe(unsigned int strobe)
{
CSN_0;
while(MISO);
SpiTxRxByte(strobe);
CSN_1;
}
//单字节读寄存器函数
unsigned int SpiReadReg(unsigned int addr)
{
unsigned int temp,value;
temp=addr|0x80;
CSN_0;
while(MISO);
SpiTxRxByte(temp);
value=SpiTxRxByte(0);
CSN_1;
return value;
}
//突发访问读寄存器函数
void SpiReadBurstReg(unsigned int addr,unsigned int *buffer,unsigned int count)
{
unsigned int i,temp;
temp=addr|0xc0;
CSN_0;
while(MISO);
SpiTxRxByte(temp);
for(i=0;i<count;i++)
{
buffer[i]=SpiTxRxByte(0);
}
CSN_1;
}
//读寄存器状态函数
unsigned int SpiReadStatus(unsigned int addr)
{
unsigned int value,temp;
temp=addr|0xc0;
CSN_0;
while(MISO);
SpiTxRxByte(temp);
value=SpiTxRxByte(0);
CSN_1;
return value;
}
//
//
//CC1100发送一组数据的函数
void RFSpiSendPacket(unsigned int *txbuffer,unsigned int size)
{
SpiWriteReg(TXFIFO,size);//是否和下句冲突?
SpiWriteBurstReg(TXFIFO,txbuffer,size);//写入要发送的数据
SpiStrobe(STX);//进入发送模式发送数据
//wait for GDO0 to be set--->sync transmitted
while(!(GDO0));
//wait for GDO0 to be cleared--->end of packet
while(GDO0);
SpiStrobe(SFTX);//冲洗 TXFIFO
}
//
//
//
//CC1100接收一组数据
unsigned int RFReceivePacket(unsigned int *rxbuffer,unsigned int *length)
{
unsigned int status[2];
unsigned int packetlength;
unsigned int i=(*length)*4;
SpiStrobe(SRX);//进入接收状态
delayus(2);
while(GDO0)
{
delayus(2);
--i;
if(i<1)
return 0;
}
if((SpiReadStatus(RXBYTES)&0x7F))//如果接的字节数不为0
{
packetlength=SpiReadReg(RXFIFO);//读出第一个字节,此字节为该帧数据长度
if(packetlength<=*length)
{
SpiReadBurstReg(RXFIFO,rxbuffer,packetlength);//读出所有接收到的数据
*length=packetlength;
SpiReadBurstReg(RXFIFO,status,2);//读出CRC校验位
SpiStrobe(SFRX);//清洗接收缓冲区
return(status[1]&0x80);//如果校验成功 返回接收成功
}
else
{
*length=packetlength;
SpiStrobe(SFRX);//清洗接收缓冲区
return 0;
}
}
else
return 0;
}