1 NRF52832SPI主機的功能描述
nRF52832SPIM的主要特征
3個SPI實例
支持SPI的模式0到模式3
支持DMA
Individual selection of IO pin for each SPI signal
注意:SPI主控制器不支持直接片選,因此SPI主機的CPU必須使用可用的GPIO來實現對從機的片選控制。另外,SPI可與和它具有相同ID的其他外設共享寄存器和其他一些資源。配置和使用SPI之前必須關閉與它有相同ID的外設。關閉與SPI有相同ID的外設,不會復位與SPI共享的寄存器。因此為了確保SPI正常運行,必須配置相關的SPI寄存器。
2 軟件設計
2.1
#define NRF_DRV_SPI_DEFAULT_CONFIG \
{ \
.sck_pin = NRF_DRV_SPI_PIN_NOT_USED, \ //SCK時鍾引腳
.mosi_pin = NRF_DRV_SPI_PIN_NOT_USED, \ //MOSI引腳
.miso_pin = NRF_DRV_SPI_PIN_NOT_USED, \ //MISO引腳
.ss_pin = NRF_DRV_SPI_PIN_NOT_USED, \ //SS引腳
.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY, \ //中斷優先級
.orc = 0xFF, \
.frequency = NRF_DRV_SPI_FREQ_4M, \ //SPI通信的速率
.mode = NRF_DRV_SPI_MODE_0, \ //SPI的工作模式
.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST, \ //MSB先行或LSB先行
}
NRF_DRV_SPI_DEFAULT_CONFIG宏用來配置SPI的基本信息。其中SPI_DEFAULT_CONFIG_IRQ_PRIORITY用來配置SPI的中斷優先級,在寫程序時要注意合理的配置中斷優先級,防止兩個外設的中斷優先級相同,在nRF52832中,中斷號為0代表中斷優先級最高。
2.2 代碼
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "boards.h"
#include "app_error.h"
#include <string.h>
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#define SPI_INSTANCE 0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); /**< SPI instance. */
static volatile bool spi_xfer_done; /**< Flag used to indicate that SPI instance completed the transfer. */
//#define TEST_STRING "Nordic"
//static uint8_t m_tx_buf[] = TEST_STRING; /**< TX buffer. */
//static uint8_t m_rx_buf[sizeof(TEST_STRING) + 1]; /**< RX buffer. */
//static const uint8_t m_length = sizeof(m_tx_buf); /**< Transfer length. */
#define W25X_WriteEnable 0x06
#define W25X_JedecDeviceID 0X9F
#define W25X_ReadStatusReg 0x05
#define W25X_SectorErase 0xD8
#define W25X_PageProgram 0x02
#define W25X_ReadData 0x03
#define W25X_ChipErase 0xC7
#define FLASH_ID 0XEF4015 //器件ID
#define Dummy_Byte 0XFF
#define WIP_Flag 0x01
#define SPI_BUFSIZE 8 //SPI緩存的大小
uint8_t SPI_Tx_Buf[SPI_BUFSIZE]; //發送
uint8_t SPI_Rx_Buf[SPI_BUFSIZE]; //接收
uint32_t ID = 0;
volatile uint8_t SPIReadLength, SPIWriteLength;
/**
* @brief SPI user event handler.
* @param event
*/
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
void * p_context)
{
spi_xfer_done = true;
}
/*
向W25Q80中寫入數據
參數 reg 寄存器地址
data 要寫入的數據
*/
void W25Q80_write_reg(int data)
{
spi_xfer_done = false;
SPIWriteLength = 1;
SPIReadLength = 0;
SPI_Tx_Buf[0] = data;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, SPI_Tx_Buf, SPIWriteLength, SPI_Rx_Buf, SPIReadLength));
while(spi_xfer_done == false);
}
/*
從W25Q80中讀取數據
參數: reg 寄存器地址
*/
uint8_t W25Q80_read_reg(int reg)
{
spi_xfer_done = false;
SPI_Tx_Buf[0] = reg;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, SPI_Tx_Buf, 0, SPI_Rx_Buf,1));
while(spi_xfer_done == false);
return SPI_Rx_Buf[0];
}
/*
讀取W25Q80的器件ID
*/
uint32_t W25Q80_ReadID(void)
{
uint32_t temp = 0,temp0 = 0,temp1 = 0,temp2 = 0;
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_JedecDeviceID);
temp0 = W25Q80_read_reg(0XFF);
temp1 = W25Q80_read_reg(0XFF);
temp2 = W25Q80_read_reg(0XFF);
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
temp = (temp0 << 16)| (temp1 << 8) | temp2;
return temp;
}
/*
寫使能命令
*/
void W25Q80_WriteEnable()
{
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_WriteEnable);
nrf_gpio_pin_set(SPI_SS_PIN); //片選有效
}
/*
通過讀狀態寄存器等待FLASH芯片空閑
*/
void W25Q80_WaitForWriteEnd()
{
unsigned char FLASH_Status = 0;
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_ReadStatusReg); //發送讀狀態寄存器
do
{
FLASH_Status = W25Q80_read_reg(Dummy_Byte);
}
while((WIP_Flag & FLASH_Status) == 1);
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
}
/*
擦除FLASH的扇區
參數 SectorAddr 要擦除的扇區地址
*/
void W25Q80_FLASH_SectorErase(uint32_t SectorAddr)
{
W25Q80_WriteEnable(); //發送FLASH寫使能命令
W25Q80_WaitForWriteEnd(); //等待寫完成
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_SectorErase); //發送扇區擦除指令
W25Q80_write_reg((SectorAddr & 0XFF0000) >> 16); //發送扇區擦除地址的高位
W25Q80_write_reg((SectorAddr & 0XFF00) >> 8);
W25Q80_write_reg(SectorAddr & 0XFF);
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
W25Q80_WaitForWriteEnd(); //等待擦除完成
}
/*
FLASH頁寫入指令
參數:
備注:使用頁寫入指令最多可以一次向FLASH傳輸256個字節的數據
*/
void W25Q80_FLASH_PageWrite(unsigned char* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
W25Q80_WriteEnable(); //發送FLASH寫使能命令
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_PageProgram); //發送寫指令
W25Q80_write_reg((WriteAddr & 0XFF0000) >> 16); //發送寫地址的高位
W25Q80_write_reg((WriteAddr & 0XFF00) >> 8);
W25Q80_write_reg(WriteAddr & 0XFF);
if(NumByteToWrite > 256)
{
NRF_LOG_INFO("write too large!\r\n");
return ;
}
while(NumByteToWrite--)
{
W25Q80_write_reg(*pBuffer);
pBuffer++;
}
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
W25Q80_WaitForWriteEnd(); //等待寫完成
}
/*
從FLASH中讀取數據
*/
void W25Q80_Flash_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_ReadData); //發送寫指令
W25Q80_write_reg((ReadAddr & 0XFF0000) >> 16); //發送寫地址的高位
W25Q80_write_reg((ReadAddr & 0XFF00) >> 8);
W25Q80_write_reg(ReadAddr & 0XFF);
while(NumByteToRead--)
{
*pBuffer = W25Q80_read_reg(Dummy_Byte);
pBuffer++;
}
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
}
/*
全片擦除
*/
void W25Q80_Chip_Erase()
{
W25Q80_WriteEnable(); //發送FLASH寫使能命令
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
W25Q80_write_reg(W25X_ChipErase); //全片擦除
nrf_gpio_pin_set(SPI_SS_PIN); //片選無效
W25Q80_WaitForWriteEnd(); //等待寫完成
}
void W25Q80_init()
{
//初始化SPI引腳
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
// spi_config.ss_pin = SPI_SS_PIN; //把SS引腳禁用
spi_config.miso_pin = SPI_MISO_PIN;
spi_config.mosi_pin = SPI_MOSI_PIN;
spi_config.sck_pin = SPI_SCK_PIN;
nrf_gpio_cfg_output(SPI_SS_PIN);
nrf_gpio_pin_clear(SPI_SS_PIN); //片選有效
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
nrf_delay_ms(500);
nrf_gpio_pin_set(15);
//讀取寄存器的值,判斷器件是否存在
ID=W25Q80_ReadID();
if(ID != FLASH_ID)
{
NRF_LOG_INFO("init w25q80 error\r\n");
}
else
{
NRF_LOG_INFO("init w25q80 ok!\r\n");
NRF_LOG_INFO("FLASH ID is %X",ID);
}
}
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define BufferSize (countof(Tx_Buffer)-1)
int main(void)
{
unsigned char Tx_Buffer[] = "This is a demo about FLASH WRITE BY Manual";
unsigned char Rx_Buffer[250];
bsp_board_init(BSP_INIT_LEDS); //初始化開發板上的指示燈
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
NRF_LOG_DEFAULT_BACKENDS_INIT();
NRF_LOG_INFO("SPI example started.");
W25Q80_init();
W25Q80_Chip_Erase(); //全片擦除 //全片擦除所需的時間比較長
W25Q80_FLASH_PageWrite(Tx_Buffer,0x00000,BufferSize);
NRF_LOG_INFO("%s\r\n",Tx_Buffer);
W25Q80_Flash_BufferRead(Rx_Buffer,0x000000,BufferSize);
NRF_LOG_INFO("%s\r\n",Rx_Buffer);
while (1)
{
// Reset rx buffer and transfer done flag
spi_xfer_done = false;
NRF_LOG_FLUSH();
nrf_delay_ms(200);
}
}
參考資料:
1 nRF52832數據手冊
2 《低功耗藍牙技術快速入門》
3 W25Q80數據手冊
4 《零死角玩轉STM32F103指南者》