位操作用于设置或读取字节中某一位或几位,包括bitRead()、bitSet()、bitClear()等,具体定义及功能可以参考文件wiring.h。
#define lowbyte(w) ((uint8-t) ((w) & 0xff)) //低字节
#define lowbyte(w) ((uint8-t) ((w) >>8)) //高字节
//读bit位的值,即保留 bit位,其他位均清零
#define bitread(value,bit) (((value)>>(bit)) & 0x01)
//置bit位的值,即 bit位置1
#define bitset(value,bit) (((value) |= (1UL<<(bit))
//清除bit位,即bit位置0
#define bitclear(value,bit) (((value) &=-(1UL<<(bit)))
//写bit位的值,1或者0
#define bitwrite(value,bit,bitvalue)(bitvalue?bitset(value,bit):bitclear(value,bit))
1.概述
SPI(Serial Peripheral Interface)是由摩托罗拉公司提出的一种同步串行外设接口总线,它可以使MCU与各种外围设备以串行方式进行通信及交换信息,总线采用3根或4根数据线进行数据传输,常用的是4根线,即两条控制线(芯片CS和时钟SCLK),以及两条数据信号线SDI和SDO。
SPI是一种高速、全双工、同步的通信总线。在摩托罗位公司的SPI技术规范中,数据信号线 SDI 称为 MISO(Master-In-Slave-Out),主入从出,数据信号线 SDO 称为 MOSI(Master-Out-Slave-In,主出从入),控制信号线 CS 称为 SS(Slave-Select,从属选择),将SCLK称为SCK(Serial-Clock,串行时钟)。在SPI通信中,数据是同步进行发送和接收的。数据传输的时钟基于来自主处理器产生的时钟脉冲,摩托罗拉公司没有定义任何通用的SPI时钟规范。
2.SPI口数据传输
SPI是以主从方式工作的,其允许一个主设备和从设备进行通信,主设备通过不同的SS信号线选择不同的从设备进行通信。其典型应用示意图如图1-49所示。
图1-49 SPI总线应用示意图
当主设备选中某一个从设备后,MISO和MOSI用于串行数据的接收和发送,SCK 提供串行通信时钟,上升沿发送,下降沿接收。在实际应用中,未选中的从设备的MOSI信号线需处于高阻状态,否则会影响主设备与选中从设备间的正常通信。
3.SPI类函数
Arduino中的SPI通信是通过SPIClass类来实现的,使用SPIClass类能够方便地将Arduino作为主设备与其他从设备进行通信。SPIClass类提供了6个成员函数供使用者调用,如下:
begin()
setBitOrder()
setClockDivider()
setDataMode()
transfer()
end()
begin函数用于初始化SPI总线,函数原型如下:
void spicLASS::begin()
{
//Set direction register for SCK and MOST pin.
//MISO pin automatically overrides to INPUT.
//When the ss pin ios set as OUTPUT,it can be used as
//a poeneral purpuse output port(it doesn’t influence
//SPIoperations).
pinmode(SCK,OUTPUT);
pinmode(MOSI,OUTPUT);
pinmode(SS,OUTPUT);
digitalWrite(SCK,LOW);
digitalWrite(MOSI,LOW);
digitalWrite(SS,HIGH);
//warnig:if the ss pin ever becomes a LOW INPUT then SPI
//automatically switches to Slave,so the data direction of
//the ss pin NUST be kept as OUTPUT.
SPCR|=-BV(MSTR);
SPCR|=-BV(SPE);
}
setBitOrder 的作用是设置串行数据传输时是先传输低位还是先传输高位,函数有一个type类型的参数bitOrder,有LSBFIRST(最低位在前)和MSBFIRST(最高位在前)两种类型可选。函数无返回值,原型如下:
void SPICLass::setBitOrder(uint8-t bit order)
{
if(bit order==KSVFURST)
{
SPCR|=BC(DORD);
}
else
{
SPCR &=-(-BV(DORD));
}
}
setClockDivder函数的作用是设置SPI串行通信的时钟,通信时钟是由系统时钟分频得到的,分频值可选2、4、8、16、32、64及128,有一个type类型的参数rate,有7种类型,对应7个分频值,分别为 SPI-CLOCK-DIV2、SPI-CLOCK-DIV4、SPI-CLOCK-DIV8、SPI-CLOCK-DVI16、SPI-CLOCK-DIV32、SPI-CLOCKL-DVI64和 ASPI-CLOCK-DVI128。函数默认参数设置是SPI-CLOCK-DVI4,设置SPI串行通信时钟为系统时钟的1/4。函数原型如下:
void spiclass::setclockdivider(uint8-t rate)
{
SPCR=(SPCR& -SPI-CLOCK-MASK)|(rate & SPI-CLOCK-MASK);
SPCR=(SPCR& -SPI-2xCLOCK-MASK)|(rate & SPI-2xCLOCK-MASK);
}
setDataMode函数的作用是设置SPI的数据模式,由于在SPI通信中没有定义任何通用的时钟规范,所以在具体应用中有的在上升沿采样,有的在下降沿采样,由此SPI存在4种数据模式,见表1-9。
表1-9 SPI通信数据模式
setDataMode 函数 type 类型的参数 mode 有4种类型可选,分别是 SPI-MODE0、SPI-MODE1、SPI-MODE2和SPI-MODE3。函数原型如下:
viod spiclass::setdatamode(uint8-t mode)
{
SPCR=(SPCR & -SPI-MODE-MASK)|mode;
}
eranfer函数用来传输一个数据,由于SPI是一种全双工、同步的通信总线,所以传输一个数据实际上会发送一个数据,同时接收一个数据。函数的参数为发送的数据值,返回的参数为接收的数据值。函数原型如下:
byte spiclass::transfer(bytre-data)
{
SPDR= -data;
while(1(SPSR & -BV(SPIF)));
return SPDR;
}
end函数停止SPI总线的使用,函数原型如下:
void SPIClass::end()
{
SPCR &=-BV(SPE));
}