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指南者》
NRF52832与W25Q80通信的更多相关文章
- SPI通信的基础知识
1 SPI物理层 SPI通信设备之间常用物理连接方式如下图 SPI通讯使用3条总线及片选线,3条总线分别为SCK.MOSI.MISO,片选线为CS. CS:从设备选择信号线,常称为片选信号线,也称 ...
- 关于NRF52832能否被替代的详解
ULP无线系统级芯片 nRF52832是用于ULP无线应用的功能强大的多协议单芯片解决方案.它结合了业界性能最佳的Nordic最新无线收发器.ARM Cortex M4F CPU和512kB闪存及64 ...
- nRF52832之硬件I2C
这几天一直在折腾nRF52832的硬件I2C,到了今天最终出现了成果,在此也印证了那句话:"耕耘就有收获" 52832的硬件I2C尽管官方提供了demo,可是自己对I2C通信理解的 ...
- IN612 IN612L蓝牙5.0 SoC芯片替换NRF52832/NRF52840
IN612L是美国公司INPLAY的SOC产品系列之一,具有多模协同2.4G无线协议栈,支持2.4G私有协议栈以及蓝牙5.0全协议栈的SOC芯片.如2mbps高数据速率模式,125kbps/500kb ...
- 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信
接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...
- 笔记:Binder通信机制
TODO: 待修正 Binder简介 Binder是android系统中实现的一种高效的IPC机制,平常接触到的各种XxxManager,以及绑定Service时都在使用它进行跨进程操作. 它的实现基 ...
- .NET 串口通信
这段时间做了一个和硬件设备通信的小项目,涉及到扫描头.输送线.称重机.贴标机等硬件.和各设备之间通信使用的是串口或网络(Socket)的方式.扫描头和贴标机使用的网络通信,输送线和称重机使用的是串口通 ...
- MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
随机推荐
- jQuery的属性操作
下面介绍jQuery属性操作: .val() 这是一个读写双用的方法,用来处理input的value,当方法没有参数的时候返回input的value值,当传递了一个参数的时候,方法修改input的va ...
- OO第一单元表达式求导作业总结
第一次作业 功能描述: 对输入的表达式进行求导计算和格式正误判断 思路: 一开始的想法是想写一个大正则找到一个通项式,通过这个多项式来判断WRONG FORMAT,结果发现正则写的总是不完善,会漏 ...
- jquery on()方法重复绑定解决方法
最近再一次项目中发现 不刷新页面的情况下使用on()方法绑定事件会出现重复执行的问题,意思就是说点击一次会绑定一次...点击n次会绑定n次,执行起来是以你绑定的次数为准,绑定了n次就会执行n次 解决办 ...
- Go 初体验 - 并发与锁.1 - sync.Mutex 与 sync.RWMutex
==== Mutex为互斥锁,顾名思义,被Mutex锁住的代码同时只允许一个协程访问,其它协程进来就要排队 如何使用?看代码: 输出: 释义: 并发1000个协程同时更改m的元素,这样会有一部分更改成 ...
- [PHP] 解决人人商城收银台不能上传图片问题
反正网上一大堆,也不知道哪个版本有修复,反正我的没有修复. 问题报错:ReferenceError: angular is not defined 解决如下: 修改文件:addons/ewei_sho ...
- JavaScript之jsx&react
1.Virtual DOM 1.将网页所有内容映射到一颗树形结构的层级对象模型上,浏览器提供对dom的支持,用户可以是用脚本调用dom,api来动态修改dom节点,从而达到修改网页目的,这种修改是浏览 ...
- Asp.Net Core WebApi 和Asp.Net WebApi上传文件
public class UpLoadController : ControllerBase { private readonly IHostingEnvironment _hostingEnviro ...
- Git 教程(三):仓库与分支
远程仓库 到目前为止,我们已经掌握了如何在Git仓库里对一个文件进行时光穿梭,你再也不用担心文件备份或者丢失的问题了. 可是有用过集中式版本控制系统SVN的童鞋会站出来说,这些功能在SVN里早就有了, ...
- 2018-2019-2 20165215《网络攻防技术》Exp6 信息搜集与漏洞扫描
目录 实验目的 实验内容 基础知识 实验步骤 (一)各种搜索技巧的应用 Google Hacking 搜索网址目录结构 搜索特定类型的文件 路由侦查 (二)DNS IP注册信息的查询 whois域名注 ...
- vue.js国际化vue-i18n插件的使用问题,在模版文本、组件方法、jsf方法里的使用
vue.js国际化vue-i18n插件的使用问题,在模版文本.组件方法.jsf方法里的使用 1.在文本里使用{{$t("xxx")}} <span>{{$t(" ...