• 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!
    ");
            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
    ");
        }
        else
        {
            NRF_LOG_INFO("init w25q80 ok!
    ");
            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
    ",Tx_Buffer);
        W25Q80_Flash_BufferRead(Rx_Buffer,0x000000,BufferSize);
        NRF_LOG_INFO("%s
    ",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指南者》

  • 相关阅读:
    axios的数据请求方式及跨域
    vuex 的介绍
    返回顶部的过渡式写法
    数据结构和算法——二叉树
    RecyclerView的刷新和加载更多
    希尔排序小结
    选择排序小结
    插入排序小结
    冒泡、快速排序小结
    数据结构和算法——递归算法
  • 原文地址:https://www.cnblogs.com/Manual-Linux/p/10628951.html
Copyright © 2020-2023  润新知