一、配置SPI引脚

本例中SPI只连接了一个设备,即国产128kB EEPROM上海贝岭BL25CMIA。

NSS脚采用软件控制。
其它引脚通过查看手册可知其连接的SPI外设为SPI1。

/* SPI port definition for master */
#define SPI_NSS_PORT                    (GPIO_PORT_B)
#define SPI_NSS_PIN                     (GPIO_PIN_12)#define SPI_SCK_PORT                    (GPIO_PORT_B)
#define SPI_SCK_PIN                     (GPIO_PIN_13)
#define SPI_SCK_FUNC                    (GPIO_FUNC_40_SPI1_SCK)#define SPI_MOSI_PORT                   (GPIO_PORT_B)
#define SPI_MOSI_PIN                    (GPIO_PIN_15)
#define SPI_MOSI_FUNC                   (GPIO_FUNC_41_SPI1_MOSI)#define SPI_MISO_PORT                   (GPIO_PORT_B)
#define SPI_MISO_PIN                    (GPIO_PIN_14)
#define SPI_MISO_FUNC                   (GPIO_FUNC_42_SPI1_MISO)

 stc_gpio_init_t stcGpioInit;/* Port configurate */(void)GPIO_StructInit(&stcGpioInit);/* High driving capacity for output pin. */stcGpioInit.u16PinDir = PIN_DIR_OUT;stcGpioInit.u16PinDrv = PIN_DRV_HIGH;stcGpioInit.u16PinState = PIN_STATE_SET;(void)GPIO_Init(SPI_NSS_PORT,  SPI_NSS_PIN, &stcGpioInit);(void)GPIO_StructInit(&stcGpioInit);stcGpioInit.u16PinDrv = PIN_DRV_HIGH;(void)GPIO_Init(SPI_SCK_PORT,  SPI_SCK_PIN, &stcGpioInit);(void)GPIO_Init(SPI_MOSI_PORT, SPI_MOSI_PIN, &stcGpioInit);/* CMOS input for input pin */stcGpioInit.u16PinDrv = PIN_DRV_LOW;stcGpioInit.u16PinIType = PIN_ITYPE_CMOS;(void)GPIO_Init(SPI_MISO_PORT, SPI_MISO_PIN, &stcGpioInit);/* Configure SPI Port function for master */GPIO_SetFunc(SPI_SCK_PORT,  SPI_SCK_PIN,  SPI_SCK_FUNC, PIN_SUBFUNC_DISABLE);GPIO_SetFunc(SPI_MOSI_PORT, SPI_MOSI_PIN, SPI_MOSI_FUNC, PIN_SUBFUNC_DISABLE);GPIO_SetFunc(SPI_MISO_PORT, SPI_MISO_PIN, SPI_MISO_FUNC, PIN_SUBFUNC_DISABLE);

二、初始化SPI1

开启SPI1时钟,给其上电。

#define SPI_UNIT_CLOCK                  (PWC_FCG1_SPI1)
    PWC_Fcg1PeriphClockCmd(SPI_UNIT_CLOCK, Enable);

根据EEPROM芯片特性,配置SPI1:

/* SPI unit and clock definition */
#define SPI_UNIT                        (M4_SPI1)
    stc_spi_init_t stcSpiInit;stc_spi_delay_t stcSpiDelayCfg;/* Clear initialize structure */(void)SPI_StructInit(&stcSpiInit);(void)SPI_DelayStructInit(&stcSpiDelayCfg);/* Configure peripheral clock */PWC_Fcg1PeriphClockCmd(SPI_UNIT_CLOCK, Enable);/* SPI De-initialize */SPI_DeInit(SPI_UNIT);/* Configuration SPI structure */stcSpiInit.u32WireMode          = SPI_WIRE_3;             //SPI只接了一个芯片,无需切换片选stcSpiInit.u32TransMode         = SPI_FULL_DUPLEX;           //全双工stcSpiInit.u32MasterSlave       = SPI_MASTER;             //SPI主机stcSpiInit.u32SuspMode          = SPI_COM_SUSP_FUNC_OFF;    //不自动挂起stcSpiInit.u32Modfe             = SPI_MODFE_DISABLE;        //请勿开启该位stcSpiInit.u32Parity            = SPI_PARITY_INVALID;      //不开奇偶校验stcSpiInit.u32SpiMode           = SPI_MODE_0;              //空闲低电平,奇数边沿采样stcSpiInit.u32BaudRatePrescaler = SPI_BR_PCLK1_DIV32;     //32分频,PCLK1为100M,32分频为3.1M,EEPROM芯片上限5MstcSpiInit.u32DataBits          = SPI_DATA_SIZE_8BIT;     //一次传输8位stcSpiInit.u32FirstBit          = SPI_FIRST_MSB;           //高位在前(void)SPI_Init(SPI_UNIT, &stcSpiInit);stcSpiDelayCfg.u32IntervalDelay = SPI_INTERVAL_TIME_1SCK_2PCLK1;//SPI_INTERVAL_TIME_8SCK_2PCLK1;   //t3:下次存取延时stcSpiDelayCfg.u32ReleaseDelay = SPI_RELEASE_TIME_1SCK;//SPI_RELEASE_TIME_8SCK;              //t2:振荡停止到片选无效时间stcSpiDelayCfg.u32SetupDelay = SPI_SETUP_TIME_1SCK;//SPI_SETUP_TIME_8SCK;                   //t1:片选有效到开始振荡时间(void)SPI_DelayTimeCfg(SPI_UNIT, &stcSpiDelayCfg);SPI_FunctionCmd(SPI_UNIT, Enable);

SPI,配置比较简单,需要说明的是:

  • stcSpiInit.u32WireMode = SPI_WIRE_3; //SPI只接了一个芯片,无需切换片选这个选3线4线都可以。注意这里的3线是指不包含NSS片选线,我们通过软件来片选。
  • stcSpiInit.u32BaudRatePrescaler = SPI_BR_PCLK1_DIV32; //32分频,PCLK1为100M,32分频为3.1M,EEPROM芯片上限5M分频时候注意对照EEPROM的芯片手册
  • t1,t2,t3的设置手册没有提及,这里设到最低。

三、读一个字节

上海贝岭BL25CMIA是一个128kB的EEPROM,其容量超过了16位寻址的上限(65536字节)。该芯片采用24位寻址,所以SPI发送地址时需要从低到高发送三个字节,其中高字节中只有第0位有效,该字节的其它位芯片does not care。

  1. 开片选
  2. 读EEPROM 的状态寄存器
#define E2PROM_READ_STATUS_REGESITER    (0x05U)
/*** @brief SPI EEPROM Read Status** @param [in] None** @retval uint8_t*/
static uint8_t E2PROM_Read_Status(void)
{uint8_t EE_Status;SPI_NSS_LOW();/* send "Read Status Register" instruction */Spi_E2PROM_WriteReadByte(E2PROM_READ_STATUS_REGESITER);/* send a dummy byte to generate the clock needed by the EEPROM and put the value of the status register in EE_Status variable */EE_Status = Spi_E2PROM_WriteReadByte(0xff);/* deselect the EEPROM */SPI_NSS_HIGH();    /* return the status register value */      return EE_Status;
}

第一个字节发指令,第二个字节随便发一个用于都回状态。
用于发送字节的函数为:

/*** @brief SPI flash write byte function** @param [in] u8Data                      SPI write data to EE** @retval uint8_t                         SPI receive data from EE*/
static uint8_t Spi_E2PROM_WriteReadByte(uint8_t u8Data)
{uint8_t u8Byte;/* Wait tx buffer empty */while (Reset == SPI_GetStatus(SPI_UNIT, SPI_FLAG_TX_BUFFER_EMPTY)){}/* Send data */SPI_WriteDataReg(SPI_UNIT, (uint32_t)u8Data);/* Wait rx buffer full */while (Reset == SPI_GetStatus(SPI_UNIT, SPI_FLAG_RX_BUFFER_FULL)){}/* Receive data */u8Byte = (uint8_t)SPI_ReadDataReg(SPI_UNIT);return u8Byte;
}
  1. 读取存储空间中指定地址内的存储值。最后关闭片选
#define E2PROM_READ_MEMORY               (0x03U)
/*** @brief SPI EEPROM Read a Byte** @param [in] 1.unsigned int** @retval uint8_t*/
uint8_t E2PROM_Read_Byte(unsigned int address, uint8_t high_or_low64)
{uint8_t data;while((E2PROM_Read_Status()&0x01) == 0x01);  //091119SPI_NSS_LOW();uint8_t addr0_7;uint8_t addr8_15;uint8_t addr16_23 = high_or_low64;addr0_7 = address & 0xff;addr8_15 = (address & 0xff00)>>8;/* send "Read Status Register" instruction */Spi_E2PROM_WriteReadByte(E2PROM_READ_MEMORY);Spi_E2PROM_WriteReadByte(addr16_23);Spi_E2PROM_WriteReadByte(addr8_15);Spi_E2PROM_WriteReadByte(addr0_7);data = Spi_E2PROM_WriteReadByte(0xff);/* deselect the EEPROM */SPI_NSS_HIGH();/* return the status register value */     return data; }

如果要读地址连续的多个字节,只需要继续发dummy字节就可以了。

四、写一个字节

  1. 开片选
  2. 读EEPROM 的状态寄存器
  3. 开启写允许
#define E2PROM_WRITE_ENABLE              (0x06U)
/*** @brief SPI EE write enable function** @param [in] None** @retval None*/
static void Spi_E2PROM_WriteEnable(void)
{SPI_NSS_LOW();(void)Spi_E2PROM_WriteReadByte(E2PROM_WRITE_ENABLE);SPI_NSS_HIGH();
}
  1. 读EEPROM 的状态寄存器
  2. 写一个字节,最后关片选
#define E2PROM_WRITE_MEMORY              (0x02U)
/*** @brief SPI EEPROM Write a Byte** @param [in] 1.unsigned int    2.uint8_t** @retval None*/
void E2PROM_Write_Byte(unsigned int address, uint8_t high_or_low64, uint8_t data)
{while((E2PROM_Read_Status()&0x01) == 0x01);  //20130528Spi_E2PROM_WriteEnable();     //091119while((E2PROM_Read_Status()&0x02) != 0x02);    //091119SPI_NSS_LOW();uint8_t addr0_7;uint8_t addr8_15;uint8_t addr16_23 = high_or_low64;addr0_7 = address & 0xff;addr8_15 = (address & 0xff00)>>8;/* send "Read Status Register" instruction */Spi_E2PROM_WriteReadByte(E2PROM_WRITE_MEMORY);Spi_E2PROM_WriteReadByte(addr16_23);Spi_E2PROM_WriteReadByte(addr8_15);Spi_E2PROM_WriteReadByte(addr0_7);Spi_E2PROM_WriteReadByte(data);/* deselect the EEPROM */SPI_NSS_HIGH();
}

写也可以写地址连续的多个字节,继续发字节就行了,最多256个,而且应从256字节(一页)的整数倍地址开始写。

最后请注意,两次写(字节或页)间隔应大于6ms,本例为8ms。

     E2PROM_Write_Byte(address, E2PROM_LOW64KB, data);SysTick_Delay(8 * SYSTICK_MINISECOND);E2PROM_Write_Byte(address + 0x8000, E2PROM_LOW64KB, data ^ CHECK_XOR1);SysTick_Delay(8 * SYSTICK_MINISECOND);

五、嵌入式的EEPROM的可靠性读写

如果应用中需要存储的数据不多,EEPROM的容量数倍于数据量,且对读写EEPROM的准确性可靠性要求比较高。应当将同一份数据及其备份均匀放在EEPROM中的多处。本例中原始数据存放在第0~127页,即0x000000到0x008000中。另外三份备份分别放在第128到255页,256到383页,384到511页。

  • 原始数据原值直接存储进去
  • 备份一经与0x3c异或后存进去
  • 备份二经与0x96异或后存进去
  • 备份三经与0x5a异或后存进去

写完以后应该立即读出来,检查是否和写入值一样。如果不一样,程序应该进行异常处理。

读取时将4个数据均读取出来。三个备份的数据读出后,再分别用0x3c,0x96,和0x5a异或(任意一个数n,与另一个数m,连续两次按位异或后等于n自己,(n ^ m) ^ m == n ),四个数中至少三个数相等才可信。如果不到三个数相等,程序应进行异常处理,比如加载默认值。

如果是整页的读写,应该使用CRC8进行校验。

同样的,整页数据也应该存入多份作为备份。本例中同样是4份,在地址中均匀分布。备份的数据同样经过上述的异或处理。并且,每一份256个字节存完以后,需要计算其CRC8,并把它也存入EEPROM中。由于数据量较大,写完以后可以不用立刻读取进行验证。

读取时,先读取原始数据页,读取后,对原始256个数据进行CRC8计算。再从EEPROM中读取原始数据的CRC8并与刚才计算的结果相比。如果结果一致,则直接使用。如果不一致,用相同的方法读取并验证备份一的,如果结果相同,则异或回来使用。如果仍然不相同,继续使用备份2……备份3的。如果所有的CRC8都不对,则应进行异常处理,比如加载默认值。

六、测试

    for (;;){        if (E2PROM_Enhanced_Write_Wave(20, t_e2dataArr) == Ok){if(E2PROM_Enhanced_Read_Wave(20, r_e2dataArr) == Ok){if (t_e2dataArr[0] == 0){for (int i=0;i<256;i++){t_e2dataArr[i] = 255-i;}}else{for (int i=0;i<256;i++){t_e2dataArr[i] = i;}}}else{GPIO_ResetPins(LED_GREEN_PORT, LED_GREEN_PIN);}}else{GPIO_ResetPins(LED_GREEN_PORT, LED_GREEN_PIN);}}

在main函数死循环内进行连续不间断读写测试,这里如果发生读写错误就点亮LED。测试3个小时,未发生错误。

华大半导体HC32F4A0笔记(四),SPI读写国产128kB EEPROM 上海贝岭BL25CMIA相关推荐

  1. 华大半导体HC32F4A0笔记(一),PWM输入捕获,使用TIM6

    一.启动时钟 PWC_Fcg2PeriphClockCmd(PWC_FCG2_TMR6_x, Enable); TMR6对应FCG2,根据PWM输入引脚来确定是TMR6_x(本例为TMR6_2).运行 ...

  2. 华大半导体HC32F4A0笔记(三),RS485通信,使用串口USART1,DMA接收

    一.USART的工作频率和波特率 看用户手册一上来就糊涂了,手册里面写的PCLK是什么?翻看手册第4章有关CMU章节.似乎这里说的PCLK就是PCLK1?手册是第一版,纰漏其实蛮多的. 在官方库函数验 ...

  3. 华大半导体HC32F4A0笔记(五),使用CMSIS-DSP库进行FFT运算

    一.开启FPU功能 点这个麻将牌四筒,展开CMSIS,把DSP勾了. 点开后 然后点这个锤子 No Auto Includes的勾不要打,让它自动include,因为CMSIS-DSP库在KEIL的安 ...

  4. 华大半导体HC32F4A0系列ARM芯片EXMC并口通信时序的FPGA实现

    华大半导体HC32F4A0系列ARM芯片EXMC并口通信时序的FPGA实现 EXMC简介 外部存储器控制器EXMC是一个用来访问各种片外存储器.实现数据交换的独立模块.EXMC通过配置可以把内部的AM ...

  5. 华大半导体 HC32F4A0 系列开发笔记

    HC32F4A0 系列开发笔记目录 选用芯片 对比STM32的优势 踩过的坑和一些总结: 1--串口空中断bug 2--标志位的寄存器 3--写IAP遇到的问题 4--CPU主频配置 5--RTC时钟 ...

  6. [芯片笔记] W25Qx芯片的标准SPI读写

    目录 1. W25Q16 1.W25Q16功能简介: 2.标准SPI读写W25Q16开发步骤 3. 标准SPI读写详细步骤 3.1 W25Q16芯片硬件说明 3.2 通过标准SPI读取开发商的ID和设 ...

  7. STM32笔记(十二)---SPI读写FLASH

    SPI读写FLASH 文章目录 SPI读写FLASH 一.SPI协议简介 1.1 SPI 物理层 1.2 协议层 1.2.1 SPI 基本通讯过程 1.2.2 通讯的起始和停止信号 1.2.3 数据有 ...

  8. STM32F103学习笔记——SPI读写Flash(二)

      此系列文章是小白学习STM32的一些学习笔记.小白第一次写笔记文章,有不足或是错误之处,请多体谅和交流! 目录 1.软件设计流程 2.SPI初始化 3.SPI发送接收一字节函数编写 4.FLASH ...

  9. [学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

    目录 9. 软件SPI读写W25Qx 0. 博主调侃: 1. 实验内容及步骤: 2. 硬件说明 3. 步骤详细讲解 3.1 配置GPIO 3.2 软件SPI读写Byte(模式0或模式3) 3.3 读取 ...

最新文章

  1. 神经网络七十年:回顾与展望
  2. SAP MM 启用批次管理的物料,在分类视图里指派023类型分类不是必须的
  3. sublime text3 jshint 安装
  4. java.vm.info_深入理解java虚拟机
  5. 洛谷 P1821 [USACO07FEB]银牛派对Silver Cow Party
  6. SharePoint 升级 Web Site 模式
  7. Android 系统文件夹功能(转)
  8. 一场游戏平台商与游戏大厂的战争
  9. 使用图片拉伸resizableImageWithCapInsets
  10. Python爬上不得姐 并将段子写入数据库
  11. jquery 祖先 parent,parents,parentUtil函数
  12. 「中间件」消息中间件如何实现每秒几十万的高并发写入?
  13. 淘宝购物车测试用例+流程图
  14. Python标准库(各种模块介绍)
  15. 用PS绘制立体字的效果教程
  16. 抛出异常关键字throw与定义异常关键字throws
  17. react中嵌入网页_react添加/嵌入 iframe
  18. Eclipse中调试Python代码--调试FWTools2.4.7中的gdal_retile.py
  19. 【看表情包学Linux】文件描述符 | 重定向 Redirection | dup2 函数 | 缓冲区的理解 (Cache)
  20. 三.N32G003 系统性能测试--dhrystone (IAR环境)

热门文章

  1. 生产者消费者模型详解以及实现
  2. 对于三大moba游戏的一点小想法
  3. ajax获取api中json数据显示到网页【带有“-”横杠注意】
  4. linux管理口连交换机灯不亮,交换机端口指示灯不亮了怎么办?
  5. C语言字符型变量sex,全国2003年4月高等教育自学考试计算机软件基础(一)试题...
  6. 某校2019专硕编程题-素数和
  7. 单芯片电容测量方案PCAP01原理
  8. C语言通过指针间接的实现函数返回多个值
  9. php fopen 指定路径,fopen 系统找不到指定路径 PHP文件包含详细讲述
  10. 软件工程文档生成工具