SPI I2C 的原理及在STM32上使用I2C总线的常见问题

发布者:ArtisticSoul最新更新时间:2019-09-03 来源: eefocus关键字:SPI  I2C  STM32  I2C总线 手机看文章 扫描二维码
随时随地手机看文章

微控制器中SPI,I2C是常用的挂接外设的总线,我们对他们的如果没有仔细研究的话我们对他们的认识基本就是串行,线少(I2C两根SCL SDA,SPI 单向3根双向4根 SCK MOSI MISO CS/LD ),多外设(SPI 可以并联也可以级联,I2C基本是随便接,地址不冲突即可),但是实际上这两种总线原理上还有有很大区别的,在微控制器上的操作及与程序控制流程也是完全不一样的。


SPI的原理 是主控使用SCLK 线,在SCLK信号的边沿 将数据寄存器的数据通过 MOSI一位一位的移出到 设备中 ,同时设备的寄存器也将数据在SCLK边沿的驱动下将数据通过MISO线一位一位的移出到主控中,所以SPI总线中读写是同时进行的,没有单独的读也没有单独的写,在微控制器端无论想读还是想写就是将数据填到DR存寄器中,MCU即自动开始传输,等待RXNE  BUSY TXE等信号置位便完成一个读或写操作。SPI状态也只有忙和不忙两种状态。


I2C总线与SPI完全不同,因为比SPI少了一根数据线和一根同步/片选线,I2C的内核涉及到状态机的问题,这也就导致了I2C的操作不像SPI那么简单。


串行通信必须有同步过程,SPI通过一根CS/LD数据线解决,I2C通过 生产START信号解决

器件的选通,SPI使用不同的CS线控制选通即可,I2C采用START+写入选通地址解决数据的传输与方向,SPI的MOSI MISO是固定线,无方向概念,I2C在根据选通地址的最低位决定是读还是写数据的确认与中止,SPI无确认,想中止拉高CS线即可,I2C需接收方使用ACK确认数据总线仲裁,SPI为固定主从不需要仲裁,I2C检查总线电平因为这些不同I2C在总线控制上比SPI要复杂的多在STM32上I2C常遇到的问题与解决办法,主要针对STM32做为主机方式


问题1: BUSY位被置位,即始使RESET后初始化总线后即被置位 不能发也不能收


出现原因:外设的不完整通信将SDA线的电平拉底,I2C内核认为总线被占用,除非总线被释放,否则拒绝操作总线(等着AT24系列自动释放总线?连RESET管脚都没有,呵呵吧)


解决办法:使用IO方式强行接管总线,以电平方式发出STOP信号 迫使设置释放总线,具体操作如下初始化前将将SCL SDL置成GPIO OUT_PP模式,拉底SCL,延时,拉低SDA,延时,抬高SCL,延时,抬高SDA 此时SCL SDA全为高电平,重新设定GPIO为ALT-OD模式再继续执行初始化I2C总线操作即可


问题2:在读取时多读一次数据


出现原因:未能正确控制NACK,与STOP位正确生成


解决办法:按手册要求,在倒数第2字节读取前便设置NACK位与STOP位,


        LL_I2C_AcknowledgeNextData(I2Cx,LL_I2C_NACK);

        LL_I2C_GenerateStopCondition(I2Cx);


问题3:如何检测AT24系列的E2ROM是否响应写操作


按手册要求在向AT24写数据过程中,写操作后面的STOP会使AT24进入内部写状态,判断是否写完成的依据是检测设备会不会在响应新的写操作,流程是START->ADDR->ACK?RESTART:STOP,可以按以下方法进行检测


写START->写 ADDR->直到检测到ADDR或AF被置位


如果AF被置位,清除AF重新执行START


如果ADDR被置位 表示操作完成,发送STOP退出查询 


注意不要用STM32中的头文件中定义的位进行比较


头文件定义如下


#define I2C_FLAG_AF                     0x00010400U

#define I2C_FLAG_ADDR                   0x00010002U


多出的那个高位1会使 如下的失效


flag  = (I2C1->SR1) | (I2C1->SR2 <<16);


if(flag & I2C_FLAG_AF) ......


最终的AT24 Pool函数如下


UINT8 I2C_Poll(UINT8 id,UINT8 devaddr)

{

    I2C_WaitBus(id);

    while(1)

    {

        I2C_Start(id);

        LL_I2C_TransmitData8(I2Cx,devaddr & 0xfe);

        GPIOB->BRR = GPIO_PIN_0;

        while(1){

            if(LL_I2C_IsActiveFlag_ADDR(I2Cx)) break;

            if(LL_I2C_IsActiveFlag_AF(I2Cx)) break;

        }

        if(LL_I2C_IsActiveFlag_AF(I2Cx)) 

        {

            LL_I2C_ClearFlag_AF(I2Cx);

            continue;

        }

        break;

    }

    LL_I2C_ClearFlag_ADDR(I2Cx);

    I2C_Stop(id);

}

关键字:SPI  I2C  STM32  I2C总线 引用地址:SPI I2C 的原理及在STM32上使用I2C总线的常见问题

上一篇:STM32常用通信——USART,IIC,SPI,CAN
下一篇:STM32小白入门(第12天)---I2C协议

推荐阅读最新更新时间:2024-11-12 20:25

STM32/ARM Cortex-M3复位序列
在离开复位状态后,CM3做的第一件事就是读取下列两个32位整数的值: 1)从地址0x0000 0000 处取出MSP的初始值 2)从地址0x0000 0004 处取出PC的初始值——这个值是复位向量,LSB必须是1.然后从这个值所对应的地址处取指,如下图1所示。 图1 请注意,这与传统的ARM架构不同——其实也和其它大多数的单片机不同。传统的ARM架构总是从0地址开始执行第一条指令,并且这是一条跳转指令。在CM3中,在0地址提供的是MSP的初始值,然后紧跟着的是向量表(向量表在以后还可以转移到其它位置)。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。 因为
[单片机]
<font color='red'>STM32</font>/ARM Cortex-M3复位序列
【话说定时器系列】之六:STM32定时器输入捕获话题
STM32定时器 是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍。去年,电堂推出了 《STM32 TIMER基础及常规应用介绍》 ,为大家梳理了 STM32 TIMER 的庞大内容,涵盖 TIMER 的基本应用原理、常规应用等。现在将课程内容整理为文章,针对STM32定时器有基本了解的用户,分享具体的应用实现环节及常见问题解决。 STM32定时器除了基本计数定时功能外,还对外拓展了输入、输出通道,从而实现输入捕捉、比较输出功能。 输入捕获【Input Capture】基本原理 : 定时器针对外部输入信号或内部触发信号实行边沿捕捉;产生捕捉事件,并可以触发中断或DMA请求,同时记录捕捉时刻计数器的值。基
[单片机]
STM32闹钟的一个进阶使用
概述: 在使用STM32的过程中,我们在项目中可能经常会用到它的闹钟功能,但是对于刚开始接触STM32闹钟时,我就是直接设置一个闹钟,然后等待中断,有时候如果有两个闹钟,我们可以用ALARM A和B,但是有4个5个或者更多的闹钟设置,这时就不知道怎么办了。我就根据我的使用需求想了一个办法(只涉及几点几分,不考虑年月日及周几),如果你也有这样的需求,可以直接使用,如果不是,也希望可以给你留下一个思考的方向。 思路: 因为我的需求是每天的几点几分有一个闹钟,然后去处理,所以我的思路也很简单,就是将所有闹钟都注册到一个数组里面,然后换算为分钟从小到大进行排序,再根据当前时间去选择我下一个要设置的闹钟是哪一个,比如我已经注册了3个闹
[单片机]
STM32学习——GPIO的操作
写在前面 1.应项目要求,要迅速熟悉stm32的GPIO、串口以及TIM高级定时器(输出PWM) 2.初步了解stm32学习方法以及编程要领(疯狂地赋值粘贴库函数,学会看库函数的函数介绍以及参数选择,注意现在h文件里看函数声明,再goto到相应的函数处) 正文 (一)GPIO的初始化过程 //主要用来点亮LED void LED_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*使能时钟,去库函数的rcc源文件里找*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
[单片机]
STM32 中JTAG 引脚作为普通IO口设置方法
第一次画STM32 的PCB ,因为采用了SWD 调试,认为JTAG的引脚PB3,PB4,没有用到就做了普通IO口,麻烦从此引起了。 设置PB3,PB4均为输出口,且输出高电平,用万用表测量,PB4为高,PB3不是高电平,在看MDK 中的寄存器值,PB3,PB4都是高啊?寄存器的值怎么和实际的值不一样了? 唉,都让我怀疑是不是引脚接错了,反复测量后发现引脚确实是对的,但为什么PB3能输出高,而PB4不可呢? 不知道,问度娘,后来在一个帖子上发现了相关回复: 首先,STM32F10x系列的MCU复位后,PA13/14/15 & PB3/4默认配置为JTAG功能。有时我们为了充分利用MCU I/O口的资源,会把这些 端口设置为
[单片机]
STM32学习笔记9——结构体赋值问题
采用TCP/IP上报采集数据,用结构体的形式建立数据缓冲区。在对结构体整体赋初值(比如帧头之类的可以先赋值)时,发现结构体只能在定义时整体赋初值,而不能在初始化语句中赋。 比如我定义的结构体为: //帧格式结构体 typedef struct { uint32_t frame_head; //帧标志 uint16_t frame_len;//帧长 stcSystemTime frame_time;//帧时间 stcFrameParameter frame_parameter;//参数列表 uint8_t frame_head_check;//帧头校验码 uint8_t frame_total_check;//整帧校验码 s
[单片机]
STM32寄存器列表 I2C相关寄存器
I2C_CR1(控制寄存器1) 15位:SWRST软件复位,当被置位时,I2C处于复位状态,在复位该位前确信I2C的引脚被释放,总线是空的,定义:0(I2C模块不处于复位状态),1(I2C模块处于复位状态) 注:该位可以用于BUSY位为’1’,在总线上又没有检测到停止条件时。 13位:ALERT-SMBus提醒,软件可以设置或清除该位;当PE=0时,由硬件清除。定义: 0(释放SMBAlert引脚使其变高。提醒响应地址头紧跟在NACK信号后面),1(驱动SMBAlert引脚使其变低。提醒响应地址头紧跟在ACK信号后面) 12位:PEC数据包出错检测,软件可以设置或清除该位;当传送PEC后,或起始或停止条件时,或当PE=
[单片机]
STM32 通用底层函数集锦, 自用
#include xustm32.h #include xucommon.h //#define COM_DEBUG #include xudebug.h //-------------------- STM32通用函数集锦 --------------------------------------------------- #if 0 HardFault_Handler PROC ; EXPORT HardFault_Handler ; B . IMPORT hard_fault_handler_c TST LR, #4 ITE EQ
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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