NRF52832与W25Q80通信

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指南者》

转载于:https://www.cnblogs.com/Manual-Linux/p/10628951.html

1. W25Q80 (8M-bit)、W25Q16 (16M-bit)和W25Q32 (32M-bit)串行闪存为空间、引脚和电源有限的系统提供了存储解决方案。25Q系列提供的灵活性和性能远远超过普通的串行闪存设备。他们是理想的代码隐藏到RAM,执行代码直接从双/四SPI (XIP)和存储声音,文本和数据。设备运行在单一2.7V至3.6V的电源上,电流消耗低至5mA有源,断电1a。所有的设备都提供节省空间的包装。W25Q80/16/32数组被组织成4,096/8,192/16,384个可编程页,每个页有256个字节。多达256字节可以被编程一次使用页程序指令。页面可以按16组(扇区擦除)、128组(32KB块擦除)、256组(64KB块擦除)或整个芯片(芯片擦除)擦除。W25Q80/16/32分别有256/512/1024个可擦除扇区和16/32/64个可擦除块。小的4KB扇区为需要数据和参数存储的应用程序提供了更大的灵活性。(见图2)。W25Q80/16/32支持标准串行外围接口(SPI),和高性能双/四输出以及双/四I/O SPI使用SPI引脚:串行时钟,芯片选择,串行数据I/O0 (DI), I/O1 (DO), I/O2 (/WP),和I/O3 (/HOLD)。SPI时钟频率高达80MHz的支持允许等效时钟率160MHz的双输出和320MHz的四次输出时,使用快速读双/四次输出指令。这些传输速率可与8位和16位并行闪存存储器相媲美。一个保持针,写保护针和可编程写保护,顶部或底部阵列控制,提供进一步的控制灵活性。此外,该设备支持JEDEC标准制造商和设备识别64位唯一序列号。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值