首先因为学习SPI的时候,仅仅只有盲写,而刚好手边有一个RC522模块,
使用的是SPI接口,因此就萌生了拿硬件来实作的念头,
既然开干就要先看好怎么接线,查了210的原理图发现,CON14已经引出了SPI0的四个接脚,只是针距是2.0mm的,
买了2.0mm转2.54的杜邦线之后,就可以正式开始,之前使用过单片机驱动RC522所以大部分的RC522代码就不多说了,
这边著重怎么设置smart210来使用spi接口
首先看一下spi_rc522.h
这个几乎是官方提供的
其中因为我使用的RC522模块还有个复位接脚,因此多定义了一个脚,
主要读写的部份我采用了接口的方式
/////////////////////////////////////////////////////////////////////
//功 能:讀RC632寄存器
//參數說明:Address[IN]:寄存器地址
//返 回:讀出的值
/////////////////////////////////////////////////////////////////////
u8_t RC522_ReadRawRC(u8_t address)
{
u8_t dat;
//片选信号低电平选中
spi0_enable();
SPI_MSBSendByte(((address<<1)&0x7E)|0x80);
dat = SPI_MSBReadByte();
//片选信号高电平取消选中
spi0_disable();
return dat;
}
/////////////////////////////////////////////////////////////////////
//功 能:寫RC632寄存器
//參數說明:Address[IN]:寄存器地址
// value[IN]:寫入的值
/////////////////////////////////////////////////////////////////////
void RC522_WriteRawRC(u8_t address, u8_t value)
{
//片选信号低电平选中
spi0_enable();
SPI_MSBSendByte(((address<<1)&0x7E));
SPI_MSBSendByte(value);
//片选信号高电平取消选中
spi0_disable();
}
简单来说就是读寄存器的时候先发送命令,然后读取返回数据,而写寄存器则是写入命令与数据,
为了更好的结构化前后加上了片选与取消片选,
接下来才是重点.....
从210手册上可以找到以上说明,也就是操作的步骤,而这份操作步骤我们应该还要加上点东西的,
我第一个想到的就是接脚的初始化,
如上图我们知道SPI0使用的是GPB的0~3脚,所以如下配置
//一、设置GPIO口使用GPB0~GPB3
GPBCON &= ~(0xFFFF << 0);
GPBCON |= (0x2222 << 0);
其中我们知道SPI的4只脚分别是SPI_0_NSS 代表片选、SPI_0_CLK代表时钟,
SPI_0_MISO 这个MISO代表主输入、从输出,SPI_0_MOSI就刚好是相反代表主输出、从输入
这样的写法相比UART的rx、tx更加清楚,因为只写rx、tx不知道板子跟模块之间到底怎么接,
而MISO跟MOSI就非常肯定到底谁是谁了
接下来按照步骤先设定 Transfer type如下图
首先我们知道SPI总共有4种工作模式,而看了RC522的模块说明,如下图
可以看出RC522的SPI模式下,为上升沿有效,对比SPI_MOD我们知道只有模式0跟模式3是在上升沿的,
找到相关寄存器说明
另外可以在初始化之前先重新置位一下,
//二、设置SPI控制器相关
//软重置一下--可有可无
CH_CFG0 |= (1<<5);
delay(100);
CH_CFG0 &= ~(1<<5);
delay(100);
所以这边可以选择模式0或模式3,并且开发板处于主模式下
//1. Set Transfer Type. (CPOL & CPHA set)
CH_CFG0 |= SPI_MOD0;
CH_CFG0 &= ~(1<<4);
第二步看了相关说明是用于避免在做为主控模式时,设备太远造成信号不稳定,所以暂时不需要
第三步要设定SPI的时钟
我的PCLK为667M,
这边千万特别注意,先使能再设定时钟,我在做的时候自作聪明把它一次写上了发现不好使,
最后经过了多次的尝试才发现是这里的问题....................
//3. Set Clock configuration register.这边设置采用PCLK 667M / (2* (99+1)),并且使能时钟
//千万注意下面两句,不可以写在一起,必须先使能再设定时钟否则不好使
CLK_CFG0 |= (1<<8);
CLK_CFG0 |= (99);
第四步设定SPI模式寄存器
//4. Set SPI MODE configuration register.
/*******************************
* 设置FIFO缓冲大小0~63字节,发送缓冲是当FIFO小于设定发生中断,接收是当数据满了发生中断
* 总线宽度保持1字节、
*********************************/
MODE_CFG0 =0;
步骤5-设定中断因为目前不使用中断方式所以暂时不管
步骤6-设定包计数器-暂时也不使用跳过
步骤7-打开接收或发送
//7. Set Tx or Rx Channel on.
//打开发送与接收
CH_CFG0 |= (0b1<<0);
步骤8-片选
我把片选移至读写的时候去做所以暂时不设定
这样初始化基本就完成了
然后撰写读写函数
void SPI0_WriteByte(unsigned char TxData)
{
int i =0;
//关闭读打开写
CH_CFG0 &= ~(0b1<<1);
delay(1);
SPI_TX_DATA0 = TxData;
while(!(SPI_STATUS0 & (1<<25)));
delay(2);
}
这边要注意的就是延迟,这个延迟必须得做
unsigned char SPI0_ReadByte(void)
{
unsigned long temp = 0;
CH_CFG0 |= (0b11);
SPI_TX_DATA0 = 0;
//這個等待必須加上,否則讀取會出錯
delay(2);
temp = (SPI_RX_DATA0 & 0xFF);
delay(2);
return (temp & 0xff);
}
最后完成代码
/***********************************************
* 档名:spi.c
* 作者:yang@wapoop.com
* 日期:2016/10/20
* 描述:spi裸机驱动程序(没有对应具体外部设备)
************************************************/
//SPI相关寄存器
#define CH_CFG0 (*((volatile unsigned long *)0xE1300000))
#define CLK_CFG0 (*((volatile unsigned long *)0xE1300004))
#define MODE_CFG0 (*((volatile unsigned long *)0xE1300008))
#define CS_REG0 (*((volatile unsigned long *)0xE130000C))
#define SPI_INT_EN0 (*((volatile unsigned long *)0xE1300010))
#define SPI_STATUS0 (*((volatile unsigned long *)0xE1300014))
#define SPI_TX_DATA0 (*((volatile unsigned long *)0xE1300018))
#define SPI_RX_DATA0 (*((volatile unsigned long *)0xE130001C))
#define PACKET_CNT_REG0 (*((volatile unsigned long *)0xE1300020))
#define PENDING_CLR_REG0 (*((volatile unsigned long *)0xE1300024))
#define SWAP_CFG_REG0 (*((volatile unsigned long *)0xE1300028))
#define FB_CLK_SEL_REG0 (*((volatile unsigned long *)0xE130002C))
//使用的GPIO口-GPB
#define GPBCON (*((volatile unsigned long *)0xE0200040))
#define GPBDAT (*((volatile unsigned long *)0xE0200044))
#define GPBPUD (*((volatile unsigned long *)0xE0200048))
#define GPBDRV (*((volatile unsigned long *)0xE020004C))
//空闲低电平上升沿数据有效
#define SPI_MOD0 (0b00 << 2)
//空闲低电平下降沿数据有效
#define SPI_MOD1 (0b01 << 2)
//空闲高电平下降沿数据有效
#define SPI_MOD2 (0b10 << 2)
//空闲高电平上升沿数据有效
#define SPI_MOD3 (0b11 << 2)
void spi0_enable(void)
{
CS_REG0 &= ~(1 << 0);
}
void spi0_disable(void)
{
CS_REG0 |= (1 << 0);
}
//初始化SPI
void spi_init(void)
{
//一、设置GPIO口使用GPB0~GPB3
GPBCON &= ~(0xFFFF << 0);
GPBCON |= (0x2222 << 0);
//二、设置SPI控制器相关
//软重置一下--可有可无
CH_CFG0 |= (1<<5);
delay(100);
CH_CFG0 &= ~(1<<5);
delay(100);
//1. Set Transfer Type. (CPOL & CPHA set)
CH_CFG0 |= SPI_MOD0;
CH_CFG0 &= ~(1<<4);
//2. Set Feedback Clock Selection register.
//这个寄存器的意义在于做为主控模式时,如果设备距离太远会导致从设备发送信号不稳定,
//所以这里可以设置一个延迟的模式,但因为现在没有实际设备暂时保持默认
//FB_CLK_SEL_REG0 = 0b1;
//3. Set Clock configuration register.这边设置采用PCLK 667M / (2* (111+1)),并且使能时钟
//千万注意下面两句,不可以写在一起,必须先使能再设定时钟否则不好使
CLK_CFG0 |= (1<<8);
CLK_CFG0 |= (99);
//4. Set SPI MODE configuration register.
/*******************************
* 设置FIFO缓冲大小0~63字节,发送缓冲是当FIFO小于设定发生中断,接收是当数据满了发生中断
* 总线宽度保持1字节、
*********************************/
MODE_CFG0 =0;
//5. Set SPI INT_EN register.--裸机暂时不使用中断
//6. Set PACKET_CNT_REG register if necessary. --启用包计数器? 应该不需要
//PACKET_CNT_REG0 |= (1<<16) | (1);
//7. Set Tx or Rx Channel on.
//打开发送与接收
CH_CFG0 |= (0b1<<0);
//8. Set nSSout low to start Tx or Rx operation. a. Set nSSout Bit to low, then start TX data writing. b. If auto chip selection bit is set, nSSout is controlled automatically.
//片选信号低电平选中
//CS_REG0 &= ~(1 << 0);
//交換高低位
//SWAP_CFG_REG0 |= (0b00110011);
}
void SPI0_WriteByte(unsigned char TxData)
{
int i =0;
//关闭读打开写
CH_CFG0 &= ~(0b1<<1);
delay(1);
SPI_TX_DATA0 = TxData;
while(!(SPI_STATUS0 & (1<<25)));
delay(2);
}
unsigned char SPI0_ReadByte(void)
{
unsigned long temp = 0;
CH_CFG0 |= (0b11);
SPI_TX_DATA0 = 0;
//這個等待必須加上,否則讀取會出錯
delay(2);
temp = (SPI_RX_DATA0 & 0xFF);
delay(2);
return (temp & 0xff);
}