1、nRF52xx SPI介绍
SPI接口基础介绍
SPI,支持全双工通信,我们这里主要介绍一下nRF52系列处理器上的SPI资源。 在nRF52832处理器中,有3个SPI 模块,SPI0 、SPI1、SPI2。 如果 SPI 作为主机使用 带有 EasyDMA 则称为 SPIM。 如果 SPI 作为从机使用 带有 EasyDMA 则称为 SPIS。
SPI的4根线:
- SDO/MOSI – 主设备数据输出,从设备数据输入;
- SDI/MISO – 主设备数据输入,从设备数据输出;
- SCLK – 时钟信号,由主设备产生;
- CS/SS – 从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高(一般来说是低电平选中)。
SPi的四种模式:
CPOL=0,表示当SCLK=0时处于空闲态,空闲低电平,所以有效状态就是SCLK处于高电平时 CPOL=1,表示当SCLK=1时处于空闲态,空闲高电平,所以有效状态就是SCLK处于低电平时 CPHA=0,表示数据采样是在第1个边沿 CPHA=1,表示数据采样是在第2个边沿
主设备能够控制时钟,因为SPI通信并不像UART或者IIC通信那样有专门的通信周期,起始信号,结束信号;所以SPI协议能够通过控制时钟信号线,当没有数据交流的时候我们的时钟线要么是保持高电平要么是保持低电平。
不同的从设备可能在出厂时由工厂配置为某种通信模式,无法改变,所以我们得根据从设备的通讯模式来设置主设备的模式。
我们还是从基本的SPI设备使用介绍开始学习,以后应用需要使用到EasyDMA再做相应的记录。
nRF52832的SPI模式:
SPI 寄存器
注意 SPI0、SPI1 和 TWI0、TWI1 共有基础地址,两者不能同时使用
SPI 库函数介绍
先熟悉一下需要用到的库函数,SPI的库函数结构 和 I2C 基本上一致
SPI初始化函数
nrf_drv_spi_init 有4个参数: 第一个参数选择使用哪个 SPI 模块(3个) 如果使用 EasyDMA 就是SPIM; 第二个参数是 SPI 的配置; 第三个参数是 自定义的事件处理函数,如果设置为NULL, 则使能 SPI 的阻塞模式; 第四个参数 写 NULL暂时,不太清楚,也是处理函数。
ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance,
nrf_drv_spi_config_t const * p_config,
nrf_drv_spi_evt_handler_t handler,
void * p_context)
nrf_drv_spi_config_t SPI默认配置如下:
#define NRF_DRV_SPI_DEFAULT_CONFIG \
{ \
.sck_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.mosi_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.miso_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \
.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \
.orc = 0xFF, \
.frequency = NRF_DRV_SPI_FREQ_4M, \
.mode = NRF_DRV_SPI_MODE_0, \
.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \
}
SPI数据传输函数
nrf_drv_spi_transfer有5个参数: 第一个参数,和上面介绍的一样,哪个SPI 模块; 第二个参数 ,指向传输缓冲区的指针; 第三个参数,为 传输缓冲区的长度; 第四个参数,指向接收缓冲区的指针; 第五个参数,为 接收缓冲区的长度;
__STATIC_INLINE
ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance,
uint8_t const * p_tx_buffer,
uint8_t tx_buffer_length,
uint8_t * p_rx_buffer,
uint8_t rx_buffer_length)
2、nRF52xx SPI 使用示例
w25qxx SPI Flash读写
这里只放Flash的驱动部分.c文件部分,至于里面的CMD,是在.h文件里面定义的,不同的SPI设备不同,这里是为了给出使用示例,所以不贴全部代码,代码是 清风蓝牙教程的demo:
w25qxx.c 驱动:
#include <string.h>
#include "nrf_drv_common.h"
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "w25q16.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "nrf_delay.h"
#define SPI_INSTANCE 0
static volatile bool spi_xfer_done;
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);
static uint8_t spi_tx_buf[256];
static uint8_t spi_rx_buf[256];
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
void * p_context)
{
spi_xfer_done = true;
}
void hal_spi_init(void)
{
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
spi_config.ss_pin = SPI_SS_PIN;
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}
uint8_t SpiFlash_ReadOneByte(void)
{
uint8_t len = 1;
spi_tx_buf[0] = 0xFF;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return (spi_rx_buf[0]);
}
void SpiFlash_WriteOneByte(uint8_t Dat)
{
uint8_t len = 1;
spi_tx_buf[0] = Dat;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
}
uint8_t SpiFlash_Write_CMD(uint8_t *CMD)
{
uint8_t len = 3;
spi_tx_buf[0] = *CMD;
spi_tx_buf[1] = *(CMD+1);
spi_tx_buf[2] = *(CMD+2);
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return RET_SUCCESS;
}
void SpiFlash_Write_Enable(void)
{
spi_xfer_done = false;
SpiFlash_WriteOneByte(SPIFlash_WriteEnable_CMD);
while(!spi_xfer_done);
}
void SPIFlash_Erase_Sector(uint8_t Block_Num,uint8_t Sector_Number)
{
SpiFlash_Write_Enable();
spi_tx_buf[0] = SPIFlash_SecErase_CMD;
spi_tx_buf[1] = Block_Num;
spi_tx_buf[2] = Sector_Number<<4;
spi_tx_buf[3] = 0x00;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, 4, spi_rx_buf, 4));
while(!spi_xfer_done);
nrf_delay_ms(10);
return ;
}
uint8_t SpiFlash_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{
uint8_t len;
SpiFlash_Write_Enable();
spi_tx_buf[0] = SPIFlash_PageProgram_CMD;
spi_tx_buf[1] = (uint8_t)((WriteAddr&0x00ff0000)>>16);
spi_tx_buf[2] = (uint8_t)((WriteAddr&0x0000ff00)>>8);
spi_tx_buf[3] = (uint8_t)WriteAddr;
memcpy(&spi_tx_buf[4],pBuffer,WriteBytesNum);
len = WriteBytesNum + 4;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, 0));
while(!spi_xfer_done);
return RET_SUCCESS;
}
uint8_t SpiFlash_Read(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
{
uint8_t len;
spi_tx_buf[0] = SPIFlash_ReadData_CMD;
spi_tx_buf[1] = (uint8_t)((ReadAddr&0x00ff0000)>>16);
spi_tx_buf[2] = (uint8_t)((ReadAddr&0x0000ff00)>>8);
spi_tx_buf[3] = (uint8_t)ReadAddr;
len = ReadBytesNum + 4;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
memcpy(pBuffer,&spi_rx_buf[4],ReadBytesNum);
return RET_SUCCESS;
}
MicroSD卡(TF卡)SPI测试
SD卡往往有多种通讯方式(上电后需要主机告诉SD卡采用什么方式,所以需要SD卡初始化程序):
- 标准的SPI
- SD模式 —— 一根线的SDIO接口
- SD模式 —— 四根线的SDIO接口,一次可以4个数据出,4个数据回
Micro SD卡SPI模式基础知识
Micro SD(TF)卡的引脚说明: Micro SD卡只有8个引脚是比SD卡少了一个Vss。可以买个SD卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,可以完全当做SD卡来操作。 在《SD卡接口规范》文档中,有SD卡工作在SPI模式的引脚定义: SD卡和Micro SD卡的SPI操作方式是一样的。
SD卡的 SPI 时钟空闲时为高电平,在时钟的第二个边沿,也就是时钟线的电平由低变高时 采集数据,所以配置 SPI 的极性和相位: CPOL = 1, CPHA = 1。由上面第一章的内容可知 读取SD卡 nRF52832工作在模式 3。
下面的图片参考博文:MicroSD卡(TF卡)SPI模式实现方法
Micro SD卡SPI 程序移植测试
研究了这么多,最好还是回到当初STM32正点原子的教程里面有SD的读写,驱动,想着直接移植过来试试,最后应该是成功了,没有做过多的测试读写,只是读了一下扇区大小。
- 程序中主要注意 读取SD卡 nRF52832工作在模式 3;
- 速度设置需要额外更改;
.h部分
typedef unsigned char u8;
typedef unsigned long int u32;
typedef unsigned int u16;
#define SD_TYPE_ERR 0X00
#define SD_TYPE_MMC 0X01
#define SD_TYPE_V1 0X02
#define SD_TYPE_V2 0X04
#define SD_TYPE_V2HC 0X06
#define CMD0 0
#define CMD1 1
#define CMD8 8
#define CMD9 9
#define CMD10 10
#define CMD12 12
#define CMD16 16
#define CMD17 17
#define CMD18 18
#define CMD23 23
#define CMD24 24
#define CMD25 25
#define CMD41 41
#define CMD55 55
#define CMD58 58
#define CMD59 59
#define MSD_DATA_OK 0x05
#define MSD_DATA_CRC_ERROR 0x0B
#define MSD_DATA_WRITE_ERROR 0x0D
#define MSD_DATA_OTHER_ERROR 0xFF
#define MSD_RESPONSE_NO_ERROR 0x00
#define MSD_IN_IDLE_STATE 0x01
#define MSD_ERASE_RESET 0x02
#define MSD_ILLEGAL_COMMAND 0x04
#define MSD_COM_CRC_ERROR 0x08
#define MSD_ERASE_SEQUENCE_ERROR 0x10
#define MSD_ADDRESS_ERROR 0x20
#define MSD_PARAMETER_ERROR 0x40
#define MSD_RESPONSE_FAILURE 0xFF
void hal_spi_init(void);
uint8_t SpiFlash_ReadOneByte(void);
void SpiFlash_WriteOneByte(uint8_t Dat);
void spi_set_highspeed(void);
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc);
u8 SD_Initialize(void);
u8 SD_GetResponse(u8 Response);
u8 SD_RecvData(u8*buf,u16 len);
u8 SD_GetCSD(u8 *csd_data);
u32 SD_GetSectorCount(void);
.c部分
#define SPI_INSTANCE 0
#define SDCARD 1
u8 SD_Type=0;
static volatile bool spi_xfer_done;
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);
static uint8_t spi_tx_buf[256];
static uint8_t spi_rx_buf[256];
static nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
void * p_context)
{
spi_xfer_done = true;
}
void hal_spi_init(void)
{
spi_config.ss_pin = SPI_SS_PIN;
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
spi_config.frequency = NRF_DRV_SPI_FREQ_250K;
#ifdef SDCARD
spi_config.mode = NRF_DRV_SPI_MODE_3;
#endif
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}
void spi_set_highspeed(void)
{
nrf_drv_spi_uninit(&spi);
spi_config.frequency = NRF_DRV_SPI_FREQ_4M;
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}
uint8_t SpiFlash_ReadOneByte(void)
{
uint8_t len = 1;
spi_tx_buf[0] = 0xFF;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return (spi_rx_buf[0]);
}
void SpiFlash_WriteOneByte(uint8_t Dat)
{
uint8_t len = 1;
spi_tx_buf[0] = Dat;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
}
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
u8 Retry=0;
SpiFlash_WriteOneByte(cmd | 0x40);
SpiFlash_WriteOneByte(arg >> 24);
SpiFlash_WriteOneByte(arg >> 16);
SpiFlash_WriteOneByte(arg >> 8);
SpiFlash_WriteOneByte(arg);
SpiFlash_WriteOneByte(crc);
if(cmd==CMD12)SpiFlash_WriteOneByte(0xff);
Retry=0X1F;
do
{
r1=SpiFlash_ReadOneByte();
}while((r1&0X80) && Retry--);
return r1;
}
u8 SD_Initialize(void)
{
u8 r1;
u16 retry;
u8 buf[4];
u16 i;
hal_spi_init();
for(i=0;i<10;i++)SpiFlash_WriteOneByte(0XFF);
retry=20;
do
{
r1=SD_SendCmd(CMD0,0,0x95);
}while((r1!=0X01) && retry--);
SD_Type=0;
if(r1==0X01)
{
if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
{
for(i=0;i<4;i++)buf[i]=SpiFlash_ReadOneByte();
if(buf[2]==0X01&&buf[3]==0XAA)
{
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0x40000000,0X01);
}while(r1&&retry--);
if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
{
for(i=0;i<4;i++)buf[i]=SpiFlash_ReadOneByte();
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;
else SD_Type=SD_TYPE_V2;
}
}
}else
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
}while(r1&&retry--);
}else
{
SD_Type=SD_TYPE_MMC;
retry=0XFFFE;
do
{
r1=SD_SendCmd(CMD1,0,0X01);
}while(r1&&retry--);
}
if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;
}
}
spi_set_highspeed();
if(SD_Type)return 0;
else if(r1)return r1;
return 0xaa;
}
u8 SD_GetResponse(u8 Response)
{
u16 Count=0xFFFF;
while ((SpiFlash_ReadOneByte()!=Response)&&Count)Count--;
if (Count==0)return MSD_RESPONSE_FAILURE;
else return MSD_RESPONSE_NO_ERROR;
}
u8 SD_RecvData(u8*buf,u16 len)
{
if(SD_GetResponse(0xFE))return 1;
while(len--)
{
*buf=SpiFlash_ReadOneByte();
buf++;
}
SpiFlash_WriteOneByte(0xFF);
SpiFlash_WriteOneByte(0xFF);
return 0;
}
u8 SD_GetCSD(u8 *csd_data)
{
u8 r1;
r1=SD_SendCmd(CMD9,0,0x01);
if(r1==0)
{
r1=SD_RecvData(csd_data, 16);
}
if(r1)return 1;
else return 0;
}
u32 SD_GetSectorCount(void)
{
u8 csd[16];
u32 Capacity;
u8 n;
u16 csize;
if(SD_GetCSD(csd)!=0) return 0;
if((csd[0]&0xC0)==0x40)
{
csize = csd[9] + ((u16)csd[8] << 8) + 1;
Capacity = (u32)csize << 10;
}else
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
Capacity= (u32)csize << (n - 9);
}
return Capacity;
}
最后测试是在主函数调用SD_GetSectorCount 函数计算扇区大小: 换了一个512MB的卡:
最后的疑问
最后还是有一个问题的,我开始读取的是 8G的卡,扇区读出来正常,但是变成 MB 就是3290MB 了,看到网上有个视频教学也是使用的这个驱动,读取8G的卡居然和我这个数据一样 3290 MB。4G和512MB的卡读出来计算出来都正常,感觉是数据类型 低级问题导致的,但是一下子还真不知道,还希望有人能够指点……
|