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

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

[复制链接]
STMCU小助手 发布时间:2022-2-1 18:00
本文介绍如何使用STM32标准外设库的GPIO端口模拟SPI,本例程使用PA5、PA6和PA7模拟一路SPI。SPI有4种工作模式,模拟SPI使用模式0,即空闲时SCK为低电平,在奇数边沿采样。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。
2 B* S0 U* j" S' q% u5 ]% Q
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。0 W2 @" U6 e( ^
SPI模式
时钟极性(空闲时SCK时钟)
时钟相位(采样时刻)
0
低电平
奇数边沿
1
低电平
偶数边沿
2
高电平
奇数边沿
3
高电平
偶数边沿

' R9 v3 N1 [* e* ^3 _. C+ h
* I( A- x) u# l3 H" j3.    时序说明
以模式0举例说明:
  • 空闲状态:片选信号SS为高,SCK输出低电平。
  • 开始信号:片选信号SS变低,SCK输出低电平。
  • 结束信号:片选信号SS变高,SCK输出低电平。
  • 读取:SCK由低变高之后,读取MISO引脚信号。
  • 写入:SCK输出低电平,MOSI引脚输出相应的电平,然后SCK输出高电平。
  • 一个时钟周期同时读取和写入:SCK输出低电平,主设备控制MOSI输出相应电平,从设备控制MISO输出相应电平,然后SCK输出高电平,从设备读取MOSI引脚电平,主设备读取MISO引脚电平。即无论主设备还是从设备,均在SCK为低电平时输出信号,在SCK为高电平时读取信号。5 y; [2 {3 Z; |' D
4.    初始化
初始化跟普通GPIO类似,SCK和MOSI设置为推挽输出,而MISO设置为浮空输入。
GPIO初始化完成之后,SCK置为低电平,进入空闲状态。
5.    模拟信号
由于SPI支持一个周期内同时读取和写入,因此读取和写入操作可以用一个函数实现,而单独的读取函数和写入函数可以通过调用该读写函数实现。
8 M9 N: |: H" R2 j$ @
完整代码(仅自己编写的部分)9 K8 @8 s8 W& }
  1. #define SPI_SCK_1    GPIO_SetBits(GPIOA, GPIO_Pin_5)            /* SCK = 1 */
    ; w  Y  X# l" o! q  ~
  2. #define SPI_SCK_0    GPIO_ResetBits(GPIOA, GPIO_Pin_5)        /* SCK = 0 */$ O  P. ^$ L: S4 l9 k% J% |2 m) O

  3. % i# _7 Y$ {0 P8 g/ i: T! ]- |
  4. #define SPI_MOSI_1    GPIO_SetBits(GPIOA, GPIO_Pin_7)            /* MOSI = 1 */
    1 U/ N; K7 I8 I! E( K
  5. #define SPI_MOSI_0    GPIO_ResetBits(GPIOA, GPIO_Pin_7)        /* MOSI = 0 */
    * T) e) V; J  v- `$ ~
  6. : _! h# S5 O. k5 g+ N2 l
  7. #define SPI_READ_MISO    GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)    /* 读MISO口线状态 */
    9 J# b8 S4 b& m: j

  8. + b8 T0 Z4 Z+ g6 {2 `
  9. #define Dummy_Byte    0xFF    //读取时MISO发送的数据,可以为任意数据. n1 ~7 B3 }3 x7 U. M2 Q
  10. # Y; `- ]7 R; G5 I! s* |# m- }8 i/ a
  11. & I$ o; \) `# x6 |  a
  12. //初始化SPI
    1 o2 S. W9 x+ |  J& m2 ]
  13. void SPI_IoInit(void)
    " ?$ n2 l- ~2 x, l7 D
  14. {
    2 V7 v* @) w* y- j% x
  15.     GPIO_InitTypeDef GPIO_InitStructure;$ K: @3 P' ]/ M5 H) }( ^

  16. : O- h% a2 }, `
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
    + b% n* O% n) Z( Q2 O
  18. * _! {1 Q8 l6 x: O4 f! q1 f& G
  19.     //CS引脚初始化
    , F) Z5 v* T0 S# Z4 }
  20.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;, e; I& C3 \  {% s% @
  21.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽输出9 u) s0 E: o8 q. V
  22.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    4 V3 t$ F3 f. z" Z! P4 S1 X5 x
  23.     GPIO_Init(GPIOC, &GPIO_InitStructure);
    $ P3 |2 S5 T& Q
  24. 6 t0 G& b% K, [7 ~! h/ P
  25.     //SCK和MOSI引脚初始化6 e5 n9 x8 N1 c5 U! f. H% [# v+ @3 p
  26.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;1 u8 `" g% \' h" G9 ^
  27.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;           //推挽输出
    ) ?7 J# Y( W1 K
  28.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 O% o+ U. Q) ~+ N
  29.     GPIO_Init(GPIOA, &GPIO_InitStructure);- ]+ M) Y; R( J0 i

  30.   B5 E4 t1 z) @* v
  31.     //MISO引脚初始化$ \% }2 b1 L& k0 I* U
  32.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;- o3 j- _+ q3 v4 x: ^, R  }
  33.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;               //浮空输入
    % r6 d) g+ I9 a% X, E, z, c6 P
  34.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    5 ?; a) G9 P$ {# K
  35. ) c* E, F( Z5 W- X
  36.     SPI_CS_1;3 [( |5 t; X& \0 z' _! S
  37.     SPI_SCK_1;0 A, N1 o2 q6 |5 ?. C$ h
  38. }$ K" }% ~- I) b7 Y& X' w
  39. " t7 A5 K5 t6 m0 x
  40. //SPI可以同时读取和写入数据,因此一个函数即可满足要求: V3 C% U2 q; l1 J" U# i
  41. uint8_t SPI_ReadWriteByte(uint8_t txData)
    * L6 _# q' \- D( E$ f
  42. {
      p! J6 Y& w' B
  43.     uint8_t i;) q3 @% O6 Z; a/ C+ k
  44.     uint8_t rxData = 0;, w* b' T# e3 L! v8 B1 U

  45. ' \' D) X% s1 J3 s
  46.     for(i = 0; i < 8; i++)
    % s5 ?/ |8 E8 v0 h$ V. r
  47.     {7 J* |8 e4 c1 E8 m' {
  48.         SPI_SCK_0;
    3 A4 G( C9 r8 D6 \" a& |
  49.         delay_us(1);% |* B1 |! R7 ?! m4 n/ z/ f
  50.         //数据发送& D' T; f9 r3 v0 ]: T$ B! v
  51.         if(txData & 0x80){/ \" f$ y/ Q( U0 a2 I  M; x: n  k
  52.             SPI_MOSI_1;. I0 W0 Q: c/ F. \  l" X4 H  d2 l
  53.         }else{/ r6 E1 W: _( S6 v! Q
  54.             SPI_MOSI_0;% A* k: z& C) @, B  o, M! @
  55.         }2 B! x2 x% e. G6 W! H
  56.         txData <<= 1;$ c- c, Q& O' p- e
  57.         delay_us(1);: b+ e( `3 |) \2 A$ C# l, r

  58. ' m' k8 ~8 u3 H& G9 G
  59.         SPI_SCK_1;
    & n4 c& r8 f9 s' e. f
  60.         delay_us(1);
      ~0 u6 {+ S+ U" l! q3 e
  61.         //数据接收
    8 h8 q' ~: r1 I  ^1 n6 |
  62.         rxData <<= 1;
      l- A- [9 [3 O0 X* V0 ?
  63.         if(SPI_READ_MISO){0 B& x6 C) d/ n, M- ~3 ~
  64.             rxData |= 0x01;
    2 b" r( L2 W' r3 H. \7 w( \$ S
  65.         }
    6 z6 ], V2 i; G, ]; Q. S' f
  66.         delay_us(1);' p  X8 R. u) \6 e* P* {, T4 k
  67.     }3 Y9 p& b* y! ^. o& b8 T& r
  68.     SPI_SCK_0;7 e' f- B" [3 S
  69. 1 ]' z! c* \8 a8 s
  70.     return rxData;
    9 v6 Z% r  Z+ H% @, h* _
  71. }
    - O7 d$ j% H1 f, S" T

  72. " X2 g# r8 Y5 T7 W* O
  73. uint8_t SPI_ReadByte(void)
    $ C  w8 J; D( c; k; Y5 a
  74. {
    % q; G- m0 R' @  Y$ v6 l
  75.     return SPI_ReadWriteByte(Dummy_Byte);
    $ S/ C7 Z: z9 g1 H# k# e
  76. }7 @# F& ^) r- ?7 G

  77. / m& f1 F: N( Z/ X4 E5 A
  78. void SPI_WriteByte(uint8_t txData)3 Q( b' M! v6 B; U% S! [
  79. {
      [8 M- Y5 w( v5 N
  80.     (void)SPI_ReadWriteByte(txData);7 s' ?5 L! ]; H, u6 Z/ p, C3 U) ]* q
  81. }
复制代码
! \. }) n; I' x% }" g+ [. ]

5 A' ?; F$ q9 e/ R7 m! V% R
赞 收藏 评论0 发布时间:2022-2-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版