固件#02--SPI原理和代码

SPI总线与AD5317R交互
本文介绍了SPI总线系统的工作原理及其在主模式和从模式下的操作方式,并通过具体实例展示了如何使用SPI与AD5317R芯片进行数据交换,包括读写操作的具体实现。

原理

简介

SPI(Serial Peripheral Interface–串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。

  • 主模式或从模式操作
  • 基于三条线的全双工同步传输
  • 基于双线的半双工同步传输,其中一条可作为双向数据线
  • 基于双线的单工同步传输,其中一条可作为单向数据线
  • 8 位到 16 位传输帧格式选

四个 I/O 引脚专用于与外部器件进行 SPI 通信。

  • MISO:主输入/ 从输出数据。通常情况下,此引脚用于在从模式下发送数据和在主模式下接收数据。
  • MOSI:主输出/ 从输入数据。通常情况下,此引脚用于在主模式下发送数据和在从模式下接收数据。
  • SCK:SPI 主器件的串行时钟输出引脚以及 SPI 从器件的串行时钟输入引脚。
  • NSS:从器件选择引脚。根据 SPI 和 NSS 设置,该引脚可用于:选择单个从器件以进行通信、同步数据帧或、检测多个主器件之间是否存在冲突。

写操作

下降沿数据有效。

读操作

模拟SPI代码

演示对芯片AD5317R的读写操作。

命令0011允许用户写入DAC寄存器并直接更新DAC输出。

例如,要回读通道A的DAC寄存器,应当实施如下操作
序列:

  1. 将0x900000写入AD5317R输入寄存器。这会将器件配置为读取模式,同时选中通道A的DAC寄存器。注意,从DB15至DB0的所有数据位都是无关位。
  2. 然后执行第二个写操作,写入NOP条件0x000000。在此写入期间,来自寄存器的数据在SDO线路上逐个输出。DB23至DB20包含未定义的数据,后16位则包含DB19至DB4 DAC寄存器内容。

spi.h


    #ifndef __SPI_H
    #define __SPI_H

    /* Includes ------------------------------------------------------------------*/
    #include "stm32l0xx_hal.h"

    #define SPI_SCK_PIN                     GPIO_PIN_4
    #define SPI_SCK_GPIO_PORT               GPIOC
    #define SPI_MOSI_PIN                    GPIO_PIN_7
    #define SPI_MOSI_GPIO_PORT              GPIOC
    #define SPI_MISO_PIN                    GPIO_PIN_8
    #define SPI_MISO_GPIO_PORT              GPIOC
    #define SPI_NSS_PIN                     GPIO_PIN_6
    #define SPI_NSS_GPIO_PORT               GPIOC

    #define SPI_SCK_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOC_CLK_ENABLE()
    #define SPI_MISO_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOC_CLK_ENABLE()
    #define SPI_MOSI_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOC_CLK_ENABLE()
    #define SPI_NSS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOC_CLK_ENABLE()


    #define MOSI_H  HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET)  
    #define MOSI_L  HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_RESET)  
    #define SCK_H   HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET)  
    #define SCK_L   HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_RESET)  
    #define MISO    HAL_GPIO_ReadPin(SPI_MISO_GPIO_PORT, SPI_MISO_PIN) 
    #define NSS_H   HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET)  
    #define NSS_L   HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_RESET) 


    #define EEPROM_DAC_START_ADDR   DATA_EEPROM_BASE+6             /* Start @ of user EEPROM area */
    #define EEPROM_DAC_END_ADDR     (EEPROM_BIAS_START_ADDR + 7)   /* End @ of user EEPROM area */
    #define IS_DAC_DATA_ADDRESS(__ADDRESS__)          (((__ADDRESS__) >= EEPROM_DAC_START_ADDR) && ((__ADDRESS__) <= EEPROM_DAC_END_ADDR))

    void SPI_Init(void);
    uint16_t SPI_ReadWrite_Byte(uint8_t cmd_addr, uint16_t data);
    void AD5317R_DAC_Write(uint8_t channel, uint16_t data);
    uint16_t AD5317R_DAC_Read(uint8_t channel);
    void AD5317R_DAC_Disable(uint8_t channel);
    void AD5317R_DAC_Enable(uint8_t channel);
    #endif

spi.c


    #include "spi.h"
    #include "main.h"
    #include "stm32l0xx_hal.h"

    void SPI_Init(void)
    {  
      /*##-1- Enable peripherals and GPIO Clocks #################################*/
      /* Enable GPIO TX/RX clock */
      SPI_SCK_GPIO_CLK_ENABLE();
      SPI_MISO_GPIO_CLK_ENABLE();
      SPI_MOSI_GPIO_CLK_ENABLE();
      SPI_NSS_GPIO_CLK_ENABLE();

      /*##-2- Configure peripheral GPIO ##########################################*/
      /* SPI SCK GPIO pin configuration  */
      GPIO_InitTypeDef GPIO_InitStruct;

      GPIO_InitStruct.Pin       = SPI_SCK_PIN;
      GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
      //GPIO_InitStruct.Pull      = GPIO_PULLDOWN;
      GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
      HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);
      HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_SET);

      /* SPI MISO GPIO pin configuration  */
      GPIO_InitStruct.Pin = SPI_MISO_PIN;
      GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
      HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);

      /* SPI MOSI GPIO pin configuration  */
      GPIO_InitStruct.Pin = SPI_MOSI_PIN;
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
      HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);
      HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN, GPIO_PIN_SET);

      GPIO_InitStruct.Pin = SPI_NSS_PIN;
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
      HAL_GPIO_Init(SPI_NSS_GPIO_PORT, &GPIO_InitStruct);
      HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET);
    }

    uint16_t SPI_ReadWrite_Byte(uint8_t cmd_addr, uint16_t data)
    {  
      uint16_t temp=0;
      NSS_L;  
      SCK_H;    
      __NOP();             /*读取第一bit数据 等待数据稳定 根据实际时钟调整*/
      for(uint8_t i=0;i<8;i++)
      {    
        if(cmd_addr&0x80) 
        {
          MOSI_H;          /*若最高位为高,则输出高*/
        }
        else
        {
          MOSI_L;          /*若最高位为低,则输出低*/    
        }    
        __NOP();
        cmd_addr <<= 1;
        SCK_L;
        temp <<= 1;        /*数据左移*/
        if(MISO)
        {
          temp++;          /*若从从机接收到高电平,数据自加一*/
        }
        SCK_H;
      }

      data <<= 6;

      for(uint8_t i=0;i<16;i++)
      {    
        if(data&0x8000) 
        {
          MOSI_H;          /*若最高位为高,则输出高*/
        }
        else
        {
          MOSI_L;          /*若最高位为低,则输出低*/    
        }    
        __NOP();
        data <<= 1;
        SCK_L;
        temp <<= 1;        /*数据左移*/
        if(MISO)
        {
          temp++;          /*若从从机接收到高电平,数据自加一*/
        }
        SCK_H;
      }
      NSS_H;
      return temp;
    }

    void AD5317R_DAC_Disable(uint8_t channel)
    {
      uint8_t data = *(__IO uint8_t *)EEPROM_DAC_START_ADDR; 
      data= data+ 1<<((channel-1)*2);
      SPI_ReadWrite_Byte(0x40, data);

      HAL_FLASHEx_DATAEEPROM_Unlock();
      HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, EEPROM_DAC_START_ADDR, data);
      HAL_FLASHEx_DATAEEPROM_Lock();
    }

    void AD5317R_DAC_Enable(uint8_t channel)
    {
      uint8_t data = *(__IO uint8_t *)EEPROM_DAC_START_ADDR;  
      data= data+ 0<<((channel-1)*2);
      SPI_ReadWrite_Byte(0x40, data);

      HAL_FLASHEx_DATAEEPROM_Unlock();
      HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_BYTE, EEPROM_DAC_START_ADDR, data);
      HAL_FLASHEx_DATAEEPROM_Lock();
    }

    void AD5317R_DAC_Write(uint8_t channel, uint16_t data)
    {
      uint8_t cmd_addr = 0x30+(1<<((channel-1)*2));
      SPI_ReadWrite_Byte(cmd_addr, data);
    }

    uint16_t AD5317R_DAC_Read(uint8_t channel)
    {
      uint8_t cmd_addr = 0x90+(1<<((channel-1)*2));
      SPI_ReadWrite_Byte(cmd_addr, 0x0000);

      uint16_t data = SPI_ReadWrite_Byte(0x00, 0x0000);
      return data>>4;
    }

### XIP-SPI闪存与普通SPI的区别及工作原理 #### 1. 定义与基本概念 XIP(eXecute In Place)是一种允许处理器直接从外部存储器执行代码的技术,而无需先将程序加载到内部RAM中。这种技术通常用于嵌入式系统中的Flash存储器访问场景[^2]。 相比之下,普通的SPI Flash仅提供一种串行接口协议,通过主机控制器发送命令地址来读取或写入数据。 --- #### 2. 工作方式对比 ##### (1) **普通SPI的工作机制** 普通SPI Flash主要依赖于标准的SPI协议进行通信。它支持单线、双线(Dual SPI)、四线(Quad SPI)等多种模式,以提升数据吞吐量。然而,在传统操作中,CPU需要先通过SPI总线将数据从Flash读取到内存中,然后再由CPU处理这些数据[^1]。 ```python # 示例:通过普通SPI读取数据并复制到RAM def read_from_spi(spi_device, address): spi_device.select() data = spi_device.read(address) spi_device.deselect() return data ``` ##### (2) **XIP-SPI的工作机制** XIP-SPI Flash则更进一步,允许CPU直接从Flash中运行指令,从而减少中间的数据搬运过程。在这种模式下,AXI Quad SPI IP核会配置特定参数,例如使用`ext_spi_clk`作为参考时钟,并将其频率设置为SPI接口所需频率的两倍,以便高效完成数据传输任务[^2]。 ```c // 示例:启用XIP模式下的函数调用 void enable_xip_mode(AXI_QSPI *qspi_core) { qspi_core->config.ext_spi_clk_enable = true; qspi_core->config.spi_clock_ratio = 2; // 设置固定频率比率 } ``` --- #### 3. 主要区别总结 | 特性 | 普通SPI | XIP-SPI | |---------------------|----------------------------------|---------------------------------| | 数据路径 | 需要经过RAM缓存 | 可直接从Flash执行 | | 性能优化 | 较低 | 更高 | | 使用场景 | 常规数据存储 | 实时操作系统启动或固件更新 | | 接口复杂度 | 简单 | 复杂 | --- #### 4. 应用领域分析 对于资源受限的嵌入式设备而言,采用XIP-SPI可以有效降低功耗并节省成本,因为减少了对外部DRAM的需求;而对于高性能计算平台来说,则可能倾向于利用高速DDR配合传统的SPI Flash实现更大的灵活性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值