当前位置:   article > 正文

stm32 GPIO模拟SPI接口实现双机通信_miso和mosi那个设置为推挽输出

miso和mosi那个设置为推挽输出

一、SPI协议简介
    一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(主机输入从机输出)、MOSI(主机输出,针对主机来说)、SCLK(时钟,主机产生)、CS(片选,一般由主机发送或者直接使能,通常为低电平有效)

SPI接口介绍

SCK:时钟信号,由主设备产生,所以主设备SCK信号为推挽输出模式,从设备的SCK信号为浮空输入模式。

CS:使能信号,由主设备控制从设备,,所以主设备CS信号为推挽输出模式,从设备的CS信号为浮空输入模式。

MOSI:主设备数据输出,从设备数据输入,所以主设备MOSI信号为推挽输出模式,从设备的MOSI信号为浮空输入模式。

MISO:主设备数据输入,从设备数据输出,所以主设备MISO信号为浮空输入模式,从设备的MISO信号为推挽输出模式。

二、四种模式(本次模拟采用的模式0)

模式0:CPOL=0,CPHA =0  SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)

模式1:CPOL=0,CPHA =1  SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)

模式2:CPOL=1,CPHA =0  SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)

模式3:CPOL=1,CPHA =1  SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

三、双机通信实现

1、GPIO引脚

PC8为CS引脚        PC10为CLK引脚        PC11为MISO引脚        PC12为MOSI引脚

2、时序

首先主机将片选拉低(使能片选),在时钟为低电平时向从机发送数据,从机通过检测MOSI线上的高低电平实现数据的接收。在时钟为高电平时主机检测MISO线上的高低电平来实现数据的接收。

主机在时钟信号为低电平时发送单字节的最高位,然后将该字节左移一位,然后将时钟信号拉高,此时从机检测到时钟信号为高电平(时钟上升沿),从而检测MOSI线上的高低电平并将得到的高低电平放到变量中。同时从机向主机发送数据,即改变MISO线上的高低电平。此过程重复8次,即可完成发送和接收一个字节的数据。

四、代码如下

1、主设备GPIO的配置

  1. void SPI_GPIO_Init(void){
  2. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体类型的变量
  3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //使能GPIOC的端口时种
  4. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_10|GPIO_Pin_12;
  5. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50Mhz
  7. GPIO_Init(GPIOC,&GPIO_InitStructure);
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  10. GPIO_Init(GPIOC,&GPIO_InitStructure);
  11. GPIO_SetBits(GPIOC,GPIO_Pin_8); //CS拉高
  12. GPIO_ResetBits(GPIOC,GPIO_Pin_10);//CLK拉低
  13. GPIO_ResetBits(GPIOC,GPIO_Pin_11);
  14. }

2、主设备向从设备写以及读

  1. /*
  2. *********************************************************************************************************
  3. * 函 数 名: SPI_WRByte
  4. * 形 参: spi; wdat:写入的数据
  5. * 返 回 值: spi读一个字节
  6. * 功能说明: 主机spi同时读写一个字节的时序 MSB
  7. *********************************************************************************************************
  8. */
  9. u8 Master_SPI_WRByte(u8 wdat)
  10. {
  11. u8 i=0,rdat=0;
  12. SPI_CS_LOW;
  13. for(i=0;i<8;i++)
  14. {
  15. if(wdat&0x80)
  16. SPI_MOSI_HIGH;
  17. else
  18. SPI_MOSI_LOW;
  19. wdat<<=1;
  20. delay_us(3);
  21. SPI_CLK_HIGH;
  22. delay_us(2);
  23. rdat<<=1;
  24. if(SPI_MISO_READ)
  25. rdat |= 0x01;
  26. delay_us(1);
  27. SPI_CLK_LOW;
  28. }
  29. SPI_CS_HIGH;
  30. return rdat;
  31. }

3、从设备GPIO设置

  1. void SPI_GPIO_Init(void){
  2. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体类型的变量
  3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //使能GPIOC的端口时种
  4. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
  5. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50Mhz
  7. GPIO_Init(GPIOC,&GPIO_InitStructure);
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_10|GPIO_Pin_12;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  10. GPIO_Init(GPIOC,&GPIO_InitStructure);
  11. GPIO_ResetBits(GPIOC,GPIO_Pin_11);
  12. }

4、从设备读写函数

  1. u8 Slave_SPI_RWByte(u8 wdat){
  2. u8 i=0,dat=0;
  3. if(!SPI_CS_READ){ //检测到片选拉低
  4. for(i=0;i<8;i++)
  5. {
  6. while(!SPI_CLK_READ); //检测到时钟上升沿的到来
  7. if(wdat&0x80)SPI_MISO_HIGH; //MSB
  8. else SPI_MISO_LOW;
  9. wdat<<=1;
  10. dat<<=1;
  11. if(SPI_MOSI_READ)dat |= 0x01;
  12. while(SPI_CLK_READ);
  13. }
  14. }
  15. return dat;
  16. }

5、头文件

  1. #define SELECT 1 //0表示主机 其它表示从机
  2. #if SELECT==0
  3. //主机
  4. //CS引脚
  5. #define SPI_CS_HIGH PCout(8)=1
  6. #define SPI_CS_LOW PCout(8)=0
  7. //CLK引脚
  8. #define SPI_CLK_HIGH PCout(10)=1
  9. #define SPI_CLK_LOW PCout(10)=0
  10. //MISO引脚
  11. #define SPI_MISO_READ PCin(11)
  12. //MOSI引脚
  13. #define SPI_MOSI_HIGH PCout(12)=1
  14. #define SPI_MOSI_LOW PCout(12)=0
  15. void SPI_GPIO_Init(void);
  16. u8 Master_SPI_WRByte(u8 wdat);
  17. #else
  18. //从机
  19. //CS引脚
  20. #define SPI_CS_READ PCin(8)
  21. //CLK引脚
  22. #define SPI_CLK_READ PCin(10)
  23. //MISO引脚
  24. #define SPI_MISO_HIGH PCout(11)=1
  25. #define SPI_MISO_LOW PCout(11)=0
  26. //MOSI引脚
  27. #define SPI_MOSI_READ PCin(12)
  28. void SPI_GPIO_Init(void);
  29. u8 Slave_SPI_RWByte(u8 wdat);
  30. #endif

6、主函数

  1. int main(void)
  2. {
  3. u8 key;
  4. delay_init(); //延时函数初始化
  5. led_init(); //初始化与LED连接的硬件接口
  6. key_init();
  7. SPI_GPIO_Init();
  8. while(1)
  9. {
  10. #if SELECT==0
  11. key=key_scan(0);
  12. switch(key){
  13. case key_up_value:{
  14. if(Master_SPI_WRByte(0x50)==0x60)LED1=!LED1;
  15. break;
  16. };
  17. case key_down_value:{
  18. break;
  19. };
  20. case key_left_value:{
  21. break;
  22. };
  23. case key_right_value:{
  24. break;
  25. };
  26. }
  27. #else
  28. Slave_SPI_RWByte(0x60);
  29. #endif

五、实验说明

本实验通过主机向从机发送0x50命令来控制从机LED1亮灭。其他应用层的使用读者可自行完成。
 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号