关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结

发布者:心连心意最新更新时间:2019-09-03 来源: eefocus关键字:stm32  通信协议  软件模拟SPI  软件模拟I2C 手机看文章 扫描二维码
随时随地手机看文章

趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议、SPI协议、I2C协议、RS485协议。大概半年前,是过了一遍的,但也只停留于读了遍代码,跑了下例程,最近又过了一遍(自己仔细的看了一遍,老师还给我们讲了一遍,自己又讲了一遍),然后还写了一遍软件模拟SPI、软件模拟I2C的代码,才彻底的懂了个皮毛 ,:)。稍微总结下吧,总结的不好,都是自己的理解,仅供参考,主要说软件模拟SPI、I2C,硬件SPI和硬件I2C就不说了。


串口协议

串口协议没什么可说的,现在常用的串口协议,是基于以前的RS232的协议,因为RS232的引脚太多而改进过来的。

物理层只用三根引脚:TXD、RXD、GND(最好接,不然有点影响),然后TXD接RXD、RXD接TXD、GND接GND(这里我第一次接错过的,所以写出来),通过有两个收发引脚就能看出,串口协议是支持全双工的;

协议层的话,就是起始信号(1个0表示)+数据包(5、6、7、8位可选)+校验位(无、0、1、奇、偶可选)+停止信号(0.5、1、1.5、2个1表示)。

串口协议,很常见,多用于打印调试信息,也比较简单;


RS485协议

RS485协议,协议层未改,只是在串口协议的物理层做了修改,外接了一个物理收发器,然后在通信双方的两条A、B线加了电阻,通过测A、B线的电压差来传送高低电平信号,所以485通信协议的特点就是抗干扰性强、传输距离远,可以组网。由于是通过A线、B线的电压差传输高低电平信号,所以是半双工的,同一时刻只能发送或者接收。物理连接方式:A线接A线,B线接B线。


如何理解半双工、全双工,我看到网上一个很好的例子可以帮助理解。就是说,把半双工通信比作是对讲机通信,全双工就是手机通信,对讲机同一时刻只能说或者是听,而手机是可以同时说、听的。


前面两个协议比较简单,而SPI协议、I2C协议稍微麻烦点,主要说一下。


SPI协议

SPI协议,多应用于ADC、DA、LCD等设备与MCU间,要求通信速率较高的场合。一般需要4根信号线,分别是MOSI、MISO、SCK、CS线。但是!敲黑板!有可能可以只用3根,当你通过SPI和DA设备通信的时候,MISO线就可以不要了,然后我们老师更夸张,说如果你只连一个DA设备的话,那么CS线也可以不要,这里我有点不敢苟同,因为毕竟片选线是控制通讯开始结束的,但这种思想是可取的了,规则是死的,人是活的,要活学活用,这里当时听到老师讲这点的时候还是有点震撼的。扯的有点远了。。。但其实,这里要说的软件模拟。


还是回到正题,接着说软件模拟SPI,当MCU的SPI外设不够用的时候,我们就会用GPIO去模拟SPI的方式,去和支持SPI的从设备通信。下面直接贴代码了,代码已经调通的了。我做的实验是用F429IGT6通过软件模拟SPI读写一款W25Q128的FLASH芯片,模拟的是模式3(CPOL=1,CPHA=1),有两点原因:①模式3的SCK空闲电平为高电平,由高电平向低电平翻转快,而且容易;②模式3在偶数边沿采样,防止第一个信号没采到。


首先,是GPIO的初始化,CS引脚、MOSI引脚、MISO引脚、SCK引脚。除了MISO引脚配成输入模式,其余三个引脚都配成输出模式(推挽输出)。


void SPI_FLASH_Init(void)

{

/*定义一个GPIO_InitTypeDef类型的(基本IO)结构体*/

  GPIO_InitTypeDef GPIO_InitStructure;

  

  /***** 使能 GPIO 时钟 *****/

 /* 使能 FLASH_SPI引脚的GPIO时钟< SPI_CS; SPI_MOSI; SPI_MISO; SPI_SCK > ( F口 )*/

  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOF, ENABLE);


/* < 配置 SPI_FLASH_SPI 引脚: SCK; MISO; MOSI > */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7  | GPIO_Pin_9;

 /* 设置引脚模式为 SPI_5 复用功能*/

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 

/* 设置引脚速率为50MHz */   

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   

  /* 设置引脚的输出类型为推挽输出*/

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  /* 设置引脚为无上拉 下拉模式*/

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  

  /* 调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO_F*/

GPIO_Init(GPIOF, &GPIO_InitStructure);

  

/* < 配置 SPI_FLASH_SPI 引脚: CS > */

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

GPIO_Init(GPIOF, &GPIO_InitStructure);


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

GPIO_Init(GPIOF, &GPIO_InitStructure);

GPIOF->BSRRL = GPIO_Pin_6;//拉高CS线

GPIO_SetBits(GPIOF, GPIO_Pin_7);//拉高时钟线,模拟模式3


}


接下来,就是基本的发送、接收函数了。延时是用Symtick来延时,为了延时精确,符合W25Q128芯片数据手册的时序,理论上应该也可以用软件延时(未尝试)。


//软件模拟SPI写(发送)

void Soft_SPI_Write(uint8_t a)

{

uint8_t cnt;

for(cnt=0;cnt<8;cnt++)

{

GPIO_ResetBits(GPIOF, GPIO_Pin_7);//拉低CLK

Delay_us(10);//这个延时时间任意,但要大于芯片数据手册上的(纳秒级的)

if(a &0x80)

{

GPIO_SetBits(GPIOF, GPIO_Pin_9);

}

else

{

GPIO_ResetBits(GPIOF, GPIO_Pin_9);

}

a <<= 1;

Delay_us(10);

GPIO_SetBits(GPIOF, GPIO_Pin_7);//拉高CLK

Delay_us(10);

}

}


//软件模拟SPI读(接收)

uint8_t Soft_SPI_Read(void)

{

uint8_t cnt;

uint8_t Rxdata = 0;

for(cnt=0;cnt<8;cnt++)

{

GPIO_ResetBits(GPIOF, GPIO_Pin_7);//拉低CLK

Delay_us(10);

Rxdata <<= 1;

if(GPIO_ReadInputDataBit(GPIOF, GPIO_Pin_8))

{

Rxdata |= 0x01;

}

GPIO_SetBits(GPIOF, GPIO_Pin_7);//拉高CLK

Delay_us(10);

}

return Rxdata;

}


然后是,W25Q128芯片的等待准备好的函数。通过读取该芯片的BUSY位是否为1(繁忙)实现,这款FLAH 是这样的,你要根据你要执行的操作找到要发送的指令是哪个,以及对应的时序,按照它的时序来,注意时序线上的时间。


void Soft_WaitFlahToBeReady(void)

{

u8 FLASH_Status = 0x01;

GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Soft_SPI_Write(0x05);

do

{

Soft_SPI_Write(0xFF);

FLASH_Status = Soft_SPI_Read();

}

while((FLASH_Status & 0x01) == 1);


printf("rn 繁忙状态位为: 0x%Xrn", FLASH_Status);

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**


}


再是,FLASH 的扇区擦除函数了,所有的FLASH每次写入前都要进行擦除。


void Soft_Flash_SectorErase(u32 EraseAddr)

{

GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Soft_SPI_Write(0x06);//写使能指令

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**


GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Soft_SPI_Write(0x20);//扇区擦除指令

Soft_SPI_Write((EraseAddr & 0xFF0000) >> 16);

Soft_SPI_Write((EraseAddr & 0xFF00) >> 8);

Soft_SPI_Write((EraseAddr & 0xFF));

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**

Soft_WaitFlahToBeReady();

}


再就是,页写入函数了,这款FLASH芯片支持三种写入方式,单字节写入、页写入(<256Bytes)、多字节写入(基于页写入)。显然,页写入方式比单字节写入快,这里我只做了页写入的方式,用于验证是否成功,多字节写入的方式可以在此方式上拓展,秉火的例程上有,可以参考。


void Soft_SPIFlashPageWrite(u8* Pbuffer,u32 Writeaddr,u16 NumberByteToWrite)

{

GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Soft_SPI_Write(0x06);//写使能指令

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**


GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Soft_SPI_Write(0x02);//写页写入指令


Soft_SPI_Write((Writeaddr & 0xFF0000) >> 16);

Soft_SPI_Write((Writeaddr & 0xFF00) >> 8);

Soft_SPI_Write((Writeaddr & 0xFF));

if(NumberByteToWrite > 256)

{

NumberByteToWrite = 256;

printf("写入的字节数大于256");

}

while(NumberByteToWrite--)

{

Soft_SPI_Write(*Pbuffer);

Pbuffer++;

}

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**

Soft_WaitFlahToBeReady();

}


最后就是读FLASH函数了,该芯片支持多字节一直读。


void Soft_SPIFlashRead(u8* Pbuffer,u32 ReadAddr,u16 NumberByteToRead)

{


GPIOF->BSRRH = GPIO_Pin_6;  //**CS低**

Delay_us(10);

Soft_SPI_Write(0x03);//写读使能指令


Soft_SPI_Write((ReadAddr & 0xFF0000) >> 16);

Soft_SPI_Write((ReadAddr & 0xFF00) >> 8);

Soft_SPI_Write((ReadAddr & 0xFF));

while(NumberByteToRead--)

{

//Soft_SPI_Write(0xFF);//这里一开始是加了的,因为SPI是全双工的,

*Pbuffer = Soft_SPI_Read();//但是加了,读就有问题,然后仔细看了时序图,发现其实不加也可以

Pbuffer++;

}

GPIOF->BSRRL = GPIO_Pin_6;  //**CS高**

}

关键字:stm32  通信协议  软件模拟SPI  软件模拟I2C 引用地址:关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结

上一篇:JTAG和SWD的禁用配置及无法下载问题解决
下一篇:STM32常用通信——USART,IIC,SPI,CAN

推荐阅读最新更新时间:2024-11-07 07:50

stm32---ADXL345
ADXL345是一款三轴加速度传感器,广泛用于手机、游戏手柄等设计。 ADXL 支持标准的 I2C 或 SPI 数字接口,自带 32 级 FIFO 存储,并且内 部有多种运动状态检测和灵活的中断方式等特性,常用I2C接口 检测轴 初始化步骤 1)上电 2)等待 1.1ms 3)初始化命令序列 4)结束 其中上电这个动作发生在开发板第一次上电的时候,在上电之后,等待 1.1ms 左右,就可以开始发送初始化序列了,初始化序列一结束, ADXL345 就 开始正常工作了 stm里的硬件电路 adxl345.c #include adxl345.h #include iic.h #include math.h #
[单片机]
stm32---ADXL345
基于STM32 MCU的太阳能-LED街灯解决方案
随着化石类能源的日益减少,以及温室气体的过度排放导致全球变暖问题越来越受到重视,人们一方面在积极开发各类可再生新能源,另一方面也在倡导节能减排的绿色环保技术。太阳能作为取之不尽、用之不竭的清洁能源,成为众多可再生能源的重要代表;而在照明领域,寿命长、节能、安全、绿色环保、色彩丰富、微型化的LED固态照明也已被公认为世界一种节能环保的重要途径。太阳能-LED街灯同时整合了这两者的优势,利用清洁能源以及高效率的LED实现绿色照明。 本文介绍的太阳能-LED街灯方案,能自动检测环境光以控制路灯的工作状态,最大功率点追踪(MPPT)保证最大太阳能电池板效率,恒电流控制LED,并带有蓄电池状态输出以及用户可设定LED工作时间等
[电源管理]
基于<font color='red'>STM32</font> MCU的太阳能-LED街灯解决方案
STM32之Bit banding
【1】Bit banding即位带操作,STM32的寄存器大都为32位,想要修改某个特定位很困难,位带操作即可解决这个问题,它是将寄存器的特定位和bit-band区域的一个32位地址绑定,也就是一对32,你对这个位带区域的32地址赋值即可控制相应位,可赋值范围是0-2的32次方,但这里赋值0和1便足够。 【2】位带操作是硬件支持还是需要软件设置? 硬件支持,只要你能根据公式找到对应地址,你就可以轻松使用位带操作!这里推荐使用宏定义,可以看这里: https://blog.csdn.net/wofreeo/article/details/82255491 【3】位带操作支持所有寄存器吗? 可见包含了
[单片机]
基于STM32和CAN总线的电动车电池管理系统设计
  随着电池能源的广泛应用,石油资源的枯竭和环境污染,电动汽车以其节能环保的优势引起越来越多的重视,在电动汽车的研究和发展上,车载电池及其管理系统的研究与制造占据着重要位置。电动汽车动力电池在应用中的主要问题表现在:生产过程中,电池的工艺,技术以及成组技术还不能保证其初始性能具有良好的一致性;使用过程中,对过充电、过放电、过温度、过电流等非常敏感,这类情况的发生会明显缩短电池寿命,甚至会导致电池报废。电池组是几十个甚至上百个单体电池串联,单体电池之间存在不一致性,随着连续的充放电循环,电池间的不一致性加剧,电池组的可用容量受容量最小的单体电池制约。对于这些情况,电池的初始性能必须要依靠企业生产工艺的优化,生产过程关键参数的控制来改
[单片机]
基于<font color='red'>STM32</font>和CAN总线的电动车电池管理系统设计
stm32快速学习6——SysTick 定时1s控制LED
设置使用外部8M晶振 设置引脚功能 设置systick为1s中断 利用systick中断就可以得到1s时间 //////////////////////////////////////////////////////////////////////////前言///////////////////////////////////////////////////////////////////////////////////////// stm32的systick通过少数的程序设置,当使用systick_config()函数之后,其载入值就是你的参数,并且自动打开中断,并将中断设为最低的优先级,将其时钟设为HCLK即系统时钟7
[单片机]
<font color='red'>stm32</font>快速学习6——SysTick 定时1s控制LED
STM32 GPIO设置
STM32的输入输出管脚有下面8种可能的配置:(4输入+2输出+2复用输出) ① 浮空输入_IN_FLOATING ② 带上拉输入_IPU ③ 带下拉输入_IPD ④ 模拟输入_AIN ⑤ 开漏输出_OUT_OD ⑥ 推挽输出_OUT_PP ⑦ 复用功能的推挽输出_AF_PP ⑧ 复用功能的开漏输出_AF_OD 1.1 I/O口的输出模式下,有3种输出速度可选(2MHz、10MHz和50MHz),这个速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信号的速度与程序有关(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路)。通过选
[单片机]
基于STM32单片机的数据记录装置设计
引言 本文针对电动汽车研究的实际需求,设计一款数据记录装置,该数据记录装置是搭建在电池能量管理系统基础上的,通过与能量管理系统通信,记录电动汽车实际运行时电池的外部状态(如:电池电压、电流、温度等),一方面为了研究电池的工作特性,另一方面为了对能量管理系统的工作情况做验证,为电动汽车动力电池的理论研究提供数据支持。 1 系统总体设计 本数据记录装置的设计包括硬件设计与软件设计两方面,软件设计主要包括数据接收的编程以及数据存储的编程,而硬件设计主要有几个方面:主控芯片的选择、复位功能的实现、电源模块、实时时钟、通信模块以及SD卡连接等。主控芯片是控制系统的核心,它内部所集成的模块越多,就能省去更多的外部电路,使得电路的设
[电源管理]
基于<font color='red'>STM32</font>单片机的数据记录装置设计
STM32 基础系列教程 1- CubeMX+GPIO
前言 学习stm32 GPIO 的使用,设置某一GPIO引脚为输出功能,将对应引脚拉高或拉低输出,同时学会初步认识STM32最新的HAL库的使用, 用代码实现控制GPIO引脚输出产生周期出1s 占空比为50%的PWM波。 示例详解 基于硬件平台: STM32F10C8T6最小系统板, MCU 的型号是 STM32F103c8t6, 使用stm32cubemx 工具自动产生的配置工程,使用KEIL5编译代码。 本示例所用的最小系统板原理图: 新建STM32 CUBEMX 工程, 双击桌面STM32CubeMX工具 在CubeMX中菜单中点 File à New Project … 在新弹出的界面
[单片机]
<font color='red'>STM32</font> 基础系列教程 1- CubeMX+GPIO
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved