你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32之IO口模拟SPI

[复制链接]
STMCU小助手 发布时间:2022-2-1 18:00
本文介绍如何使用STM32标准外设库的GPIO端口模拟SPI,本例程使用PA5、PA6和PA7模拟一路SPI。SPI有4种工作模式,模拟SPI使用模式0,即空闲时SCK为低电平,在奇数边沿采样。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。

1.   简介
SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在要求通讯速率较高的场合。SPI用于多设备之间通讯,分为主机Master和从机Slave,主机只有一个,从机可以有多个,通过片选信号对从机进行选择,一次只能选择一个从机。通讯只能由主机发起,支持的操作分为读取和写入,即主机读取从机的数据,以及向从机写入数据。
SPI一般有4根线,分别是片选线SS、时钟线SCK、主设备输出\从设备输入MOSI、主设备输入\从设备输出MISO,其中除MISO对于主机为输入引脚外,其他引脚对于主机均为输出引脚。因为有独立的输入和输出引脚,因此SPI支持全双工工作模式,即可以同时接收和发送。
2.    总线传输信号
  • 空闲状态:片选信号SS低电平有效,那么空闲状态片选信号SS为高。
  • 开始信号及结束信号:开始信号需要将片选信号SS拉低,结束信号需要将片选信号SS拉高。
  • 通讯模式:SPI有4种通讯模式,分别为0、1、2、3,根据时钟极性和时钟相位确定,时钟极性分别为空闲低电平和空闲高电平,时钟相位分别为SCK奇数边沿采样和偶数边沿采样。常用的模式为模式0和模式3。
SPI模式
时钟极性(空闲时SCK时钟)
时钟相位(采样时刻)
0
低电平
奇数边沿
1
低电平
偶数边沿
2
高电平
奇数边沿
3
高电平
偶数边沿


3.    时序说明
以模式0举例说明:
  • 空闲状态:片选信号SS为高,SCK输出低电平。
  • 开始信号:片选信号SS变低,SCK输出低电平。
  • 结束信号:片选信号SS变高,SCK输出低电平。
  • 读取:SCK由低变高之后,读取MISO引脚信号。
  • 写入:SCK输出低电平,MOSI引脚输出相应的电平,然后SCK输出高电平。
  • 一个时钟周期同时读取和写入:SCK输出低电平,主设备控制MOSI输出相应电平,从设备控制MISO输出相应电平,然后SCK输出高电平,从设备读取MOSI引脚电平,主设备读取MISO引脚电平。即无论主设备还是从设备,均在SCK为低电平时输出信号,在SCK为高电平时读取信号。
4.    初始化
初始化跟普通GPIO类似,SCK和MOSI设置为推挽输出,而MISO设置为浮空输入。
GPIO初始化完成之后,SCK置为低电平,进入空闲状态。
5.    模拟信号
由于SPI支持一个周期内同时读取和写入,因此读取和写入操作可以用一个函数实现,而单独的读取函数和写入函数可以通过调用该读写函数实现。

完整代码(仅自己编写的部分)
  1. #define SPI_SCK_1    GPIO_SetBits(GPIOA, GPIO_Pin_5)            /* SCK = 1 */
  2. #define SPI_SCK_0    GPIO_ResetBits(GPIOA, GPIO_Pin_5)        /* SCK = 0 */

  3. #define SPI_MOSI_1    GPIO_SetBits(GPIOA, GPIO_Pin_7)            /* MOSI = 1 */
  4. #define SPI_MOSI_0    GPIO_ResetBits(GPIOA, GPIO_Pin_7)        /* MOSI = 0 */

  5. #define SPI_READ_MISO    GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)    /* 读MISO口线状态 */

  6. #define Dummy_Byte    0xFF    //读取时MISO发送的数据,可以为任意数据


  7. //初始化SPI
  8. void SPI_IoInit(void)
  9. {
  10.     GPIO_InitTypeDef GPIO_InitStructure;

  11.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);

  12.     //CS引脚初始化
  13.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  14.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽输出
  15.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16.     GPIO_Init(GPIOC, &GPIO_InitStructure);

  17.     //SCK和MOSI引脚初始化
  18.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
  19.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽输出
  20.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  21.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  22.     //MISO引脚初始化
  23.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;               //浮空输入
  25.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  26.     SPI_CS_1;
  27.     SPI_SCK_1;
  28. }

  29. //SPI可以同时读取和写入数据,因此一个函数即可满足要求
  30. uint8_t SPI_ReadWriteByte(uint8_t txData)
  31. {
  32.     uint8_t i;
  33.     uint8_t rxData = 0;

  34.     for(i = 0; i < 8; i++)
  35.     {
  36.         SPI_SCK_0;
  37.         delay_us(1);
  38.         //数据发送
  39.         if(txData & 0x80){
  40.             SPI_MOSI_1;
  41.         }else{
  42.             SPI_MOSI_0;
  43.         }
  44.         txData <<= 1;
  45.         delay_us(1);

  46.         SPI_SCK_1;
  47.         delay_us(1);
  48.         //数据接收
  49.         rxData <<= 1;
  50.         if(SPI_READ_MISO){
  51.             rxData |= 0x01;
  52.         }
  53.         delay_us(1);
  54.     }
  55.     SPI_SCK_0;

  56.     return rxData;
  57. }

  58. uint8_t SPI_ReadByte(void)
  59. {
  60.     return SPI_ReadWriteByte(Dummy_Byte);
  61. }

  62. void SPI_WriteByte(uint8_t txData)
  63. {
  64.     (void)SPI_ReadWriteByte(txData);
  65. }
复制代码


收藏 评论0 发布时间:2022-2-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版