当前位置:首页 > iic
  • 电机驱动电路原理你值得了解一下?

    电机驱动电路原理你值得了解一下?

    下面小编为大家整理了电机驱动电路原理,你值得收藏! 电机驱动电路原理如图 2-1 所示: 图 2-1 中 Header 4X2 为 4 排 2 列插针,FM0~3 为 FPGA 芯片 I/O 输出口,加入的插针给予一个可动的机制,在需要使用时才用跳线帽进行相连,提高 I/O 口的使用效率。 RES5 是五端口排阻,内部集成了 4 个等阻值且一端公共连接的电阻,PIN 1 是公共端,PIN2~5 为排阻的输出端,排阻原理图如图 2-2 所示: 图 2-2 该排阻公共端接电源,即上拉电阻形式,作用是增强 FPGA 芯片 I/O 口(以下简称 I/O 口)的驱动能力,实际上就是增加 I/O 输出高电平时输出电流的大小。当 I/O 输出高电平时,+5V 电源经排阻与 IN1~4 相连,相当于为 I/O 提供一个额外的电流输出源,从而提高驱动能力。当 I/O 输出低电平时,可将 I/O 近似看做接地,而 IN1~4 因与 I/O 由导线直接相连,因此直接接受了 I/O 的低电平输出信号。此时,+5V 电源经排阻 R、I/O 内部电路(电阻近似为零)后接地,因此该路的电流不能大于 I/O 的拉电流(Ii)最大值,有公式 2-1: 由公式 2-2 可以得出排阻的取值范围。 该上拉电阻除了提高驱动能力外,还有一个作用,就是进行电平转换。经查,ULN2003 的接口逻辑为:5V-TTL, 5V-CMOS 逻辑。而在 3.3V 供电的情况下,I/O 口可以提供 3.3V-LVTTL,3.3V-LVCMOS,3.3V-PCI 和 SSTL-3 接口逻辑电平。因此,需要外接 5V 的上拉电阻将 I/O 电平规格变成 5V 电平逻辑。 芯片 ULN2003 内部集成 7 组达林顿管,专门用于提高驱动电流,芯片引脚间逻辑如图 2-3 所示: 由于 I/O 电流远远不足以驱动电机,因此需要外接该芯片驱动电机,ULN2003 内部集成的达林顿管电路如图 2-4 所示。达林顿管的形式具有将弱点信号转化成强电信号的特点,I/O 电平逻辑从 PIN IN 输入,通过达林顿管控制 PIN 9(COMMON)端输入的强电信号按照 I/O 信号规律变化。值得注意的是:ULN2003 输出逻辑将与输入逻辑相反,编程时应该注意该特点。 RES6 是六端口排阻,内部集成了 5 个等阻值且一端公共连接的电阻,PIN 1 是公共端,PIN2~6 为排阻的输出端,原理图与接法说明可参考上述图 2-2,排阻取值范围计算参见公式 2-2,此处不再赘述。值得注意的是:RES6 的 PIN 1 与 PIN 2 相连,是因为多出了一个不使用的电阻,为了避免 PIN 2 悬空,因此将 PIN 2 与 PIN 1(公共端)相连,即 PIN 2 对应的电阻被短路,从而既避免的悬空的引脚,又能使该电阻失效。

    时间:2020-10-20 关键词: 电机驱动电路原理 电源电路 iic

  • 迪康将于2012IIC-China上展出最新产品

    派瑞特旗下品牌,数字电视与广播接收器件的领先提供商迪康,将于2月23-25日出席在深圳举行的2012年国际集成电路研讨会暨展览会(IIC-China),展出用于数字电视与广播的可编程接收芯片Octopus 2的最新版本。 Octopus 2是首款可同时接收CMMB和CTTB(或称DTMB)标准信号的芯片。这一新型方案可以有效满足中国汽车市场高速增长的需求(年均增速20%),而这一市场正在获得来自于对嵌入式多媒体,如广播电视应用强劲需求的支持。 通过调用相应的微码,可以接收特定标准的信号。因此,同一器件可以使用两组微码在中国各地接收CMMB或CTTB标准的数字电视信号。目前全国各地已有300多个城市开通了数字电视服务。 此外,依靠其它微码,这一器件还能够接收欧洲(DVB-T, DAB, T-DMB)、日本、拉丁美洲(ISDB-T)以及美国(ATSC, ATSC-M/H)的数字电视和广播。 和前代产品一样,新一代Octopus芯片包含一个多频调谐器、一个专利的可编程解调器、一个付费电视解扰器和智能电源管理功能。 迪康数字调谐器业务部执行副总裁Yannick Lévy表示:“Octopus的低成本、灵活性和在移动中表现出的高性能源自于复杂的算法,使该芯片成为汽车设备生产商理想的渐进式解

    时间:2020-09-08 关键词: 迪康 octop iic

  • 明星也对区块链有兴趣?

    明星也对区块链有兴趣?

    明星涉及区块链早已不是什么新鲜事:韩国顶级女团T-ara在韩国发布了T-ara币;韩流明星权志龙也在自己的演唱会售票通道上开启了区块链token兑换区。而国内明星中,汪峰也在节目后台大谈区块链技术,甚至立下“我一定要成为中国第一个发币的艺人”的flag。最近又有一位明星疑似涉足区块链项目,那就是最近正在准备演唱会的王力宏。 王力宏全球巡演北京站门票开售仅58秒便被粉丝们抢购一空,而就在粉丝为抢不到票而苦恼时,一家名为智投链IIC的区块链项目宣布:可以使用其token兑换王力宏演唱会的门票!这到底是什么来头? 据知情人士爆料,王力宏厦门当代东方的签约艺人,而厦门当代集团正是智投链IIC的投资方之一… 那么,Leehom会不会也和IIC互粉了呢? 据悉,智投链IIC是以区块链技术整合金融科技Fintech、大数据分析、人工智能、机器算法等技术能力,帮助投资者快速做出正确投资决策和决策执行的智能投顾平台。平台专注于数字资产、股票、债券、外汇以及金融衍生品的管理和运营,智投币IIC可作为token在平台上训练、购买各种投资策略;智投链平台本身更像是一个投资策略的市场,财富思维的数字世界。 此外,智投链IIC控股的智嬴通平台,可一键接入当前主流交易所(目前已可接入20家),提供包括开户、交易、套利等在内的一站式服务。通过一站式、规范化服务聚集用户,成为连接投资者和数字资产交易所的桥梁。 王力宏是否智投链IIC的投资人一事尚未得到证实,但是token兑换门票的活动却是真有其事,该活动目前已经在火币HADAX和BCEX上线宣传,本周末会继续上线一个新交易所比特亚洲,2018年3月31日开放充值,听说会免除前15个交易日内的交易手续费噢! 不管你是王力宏的铁杆粉丝,还是想要体验一把数字货币世界与真实场景交融的瞬间,都可以来试试手气哦! 活动规则具体如下: 活动期间参与智投链IIC交易或通过智投链IIC兑换,满足以下任意条件即可领取王力宏龙的传人演唱会北京场门票(注: 本次活动共有300张王力宏龙的传人演唱会门票,2018年4月13、14日各有150张,其中外场区、中场区及内场区各50张/场;): 1、通过活动链接,提交智投链IIC的持仓截图(imtoken钱包、火币、bcex持仓截图均有效),我们会抽出5名幸运用户(中奖概率=您的持仓总和/报名活动的持仓总和)。 【奖励规则】: 获奖名额: 5名 每人奖励王力宏北京演唱会门票2张 公布日期: 2018年4月6日12:00 公布平台:智投链官方服务号:iic_chain    智投链官方订阅号:iichain 2、智投链IIC代币兑换王力宏龙的传人演唱会专属门票,如需获取智投链IIC可登录火币网(https://www.hadax.com/zh-cn/iic_eth/exchange/)或BCEX(https://www.bcex.top/trade/iic2eth)进行购买 【兑换规则】: 18000IIC兑换外场区坐席/张 24000IIC兑换中场区坐席/张 32000IIC兑换内场区坐席/张 3、如果存在刷票、提供虚假信息等行为主办方将有权取消门票的获取资格,本次最终解释权归主办方所有。

    时间:2020-07-22 关键词: 区块链 iic

  • IIC 协议分析

    IIC 协议分析

    1. 协议基础 1.1. 协议简介 IIC-BUS(Inter-IntegratedCircuit Bus)最早是由PHilip半导体(现在被NXP收购)于1982年开发。主要是用来方便微控制器与外围器件的数据传输。它是一种半双工,由SDA(数据)和SCL(时钟)组成的两线式串行传输总线。目前最新的协议版本是2014版,官方链接如下:https://www.nxp.com/docs/en/user-guide/UM10204.pdf 1.2. 物理信号 图1-1 IIC实际的波形 图1-2 IIC协议基本形式 IIC由一条时钟线和一条数据线组成。如图1-1是示波器抓取的实际信号、1-2是IIC协议数据传输的基本形式。 IIC的输入输出结构采用的是开漏的结构。开漏结构不能够自主得到高电平,所以需要通过外部上拉电阻Rp来的实现IIC通信过程中的高电平。Rp的大小取决于IIC不同模式时的灌电流大小。 图1-3和图1-4是描述IIC获得高低电平的情景。因为一条IIC总线上面可能会同时连接上多个设备,如果IIC使用的是推挽输出的话容易引起短路。IIC设备可以通过控制N-MOS管的开关来控制输出信号的电平高低。当MOS管G极为低电平时MOS管截止IIC总线上面由于有上拉电阻的存在而为高电平;当MOS管G极为高电平时MOS管导通,IIC总线相当于直接接地为低电平。 IIC的输入是通过TTL肖特基触发器将数据传输到输入数据寄存器当中,再提供给处理器处理。 图1-3 图1-4 IIC的电平标准: 由于种类的设备都有可能连接到IIC总线上面,比如说CMOS、NMOS等,所以IIC的高电平和低电平的标准是不一定的。高电平和低电平的值分别为0.7VDD和0.3VDD。 图1-5 1.3. 总线连接 图1-6 IIC的时钟线总是由主机控制,主机与从机之间的数据传输只在SDA一根线上完成,不能同时发送和接收数据,所以IIC是一种半双工的通信协议。一条IIC总线上面可以挂载多个设备,每一个设备都有其对应的设备地址,设备之间数据传送只能由一个设备传送给对应地址的设备。 图1-7 IIC的总线连接可以接受多主机的模式,也就是说一条IIC总线上面可以有多个设备可以作为主机来使用,但是在一次数据的传输过程中只能有一个设备作为主机。一条IIC总线上面谁是主机取决于总线上面的时钟和数据信号由谁控制。如图1-7所示,这一条IIC总线上面挂载了多个设备,其中MCU1和MCU2都可以作为主机来使用,但是不能同时有两个主机。 如果两个MCU同时发起开始信号时(都试图成为主机),这时候IIC的仲裁机制会发挥作用来判定谁成为主机。 图1-8 IIC的仲裁机制得益于其开漏的输入输出结构。例如如图1-8所示,当SCL线上挂载的多个设备,其中的MCU2的SCL输出低电平,那么这条IIC总线SCL就会被MCU2拉低,这也就是“与”的特性。 IIC上的仲裁主要是由两部分组成SCL时钟同步、SDA线仲裁。 图1-9 如图1-9所示CLK1和CLK2都是连接在一条SCL线上的设备同时产生的时钟信号,由于IIC总线存在“与”的特性,所以两个设备高电平相同的部分形成了SCL最终的时钟,也就是说同一条IIC总线上面的时钟都是相同的。 图1-10 同样SDA仲裁也是基于“与”的特性。如图1-10所示当两个设备同时发出开始信号想要传送数据时,在第一个和第二个周期内DATA1和DATA2的数据都是相同的,然后两者继续传送数据,当在第三个时钟周期时DATA2与SDA的数据不一致,这个时候设备2就会停止发送数据,转而启动接收模式。这样SDA的数据就会与DATA1的数据保持一致,并且设备2停止发送数据也不会影响SDA的数据。 2. 协议规范 2.1. 编码规则 起始、停止条件:IIC的起始信号为当时钟信号线(SCL)为高电平时,数据线(SDA)产生一个下降沿,停止信号为当时钟信号线(SCL)为高电平时,数据线(SDA)产生一个上升沿。 图2-1 应答位、非应答位:当主机传送8位数据结束后,主机会将SDA线拉高,此时如果从机正确接收数据则会将SDA拉低并在SCL高电平时保持低电平,这个信号为ACK信号。如果在传输8位数据后从机没有将SDA拉低则该信号为NACK。如果出现NACK则表示数据传输出错。 图2-2 数据有效性:当时钟信号为高电平的时候,数据线上的信号需要保持不变也就是在时钟线为高电平的时候数据线出现上升下降沿的话就会产生停止和启动信号,从而导致数据的传输出错。 图2-3 byte组织:SDA上的数据传输是以8bit即一个字节为单位传输的,每一次传输的字节数没有限制,每传输完一个字节后必须跟随一个应答位。 我们以01001001(0X49)为例,其时序图如下: 图2-4 2.2. 信号传输 IIC总线上面的每一个设备都有唯一的地址与之对应,信号传输时也是根据指定的地址找到设备来传输信号。 写操作:主机确定了从机的设备地址后,生成一个开始信号,然后向IIC总线上面发送设备的地址和读写方向标志。从机检测到该地址和自己设备地址相对应后,回复主机一个应答信号。主机接收到应答信号后就开始向这个设备以字节为单位发送数据,每一个字节后面都会带有从机的应答信号,直到主机发送完成最后一个数据后生成一个停止信号结束此次数据的传输。 图2-5 读操作:读操作与写操作有一些类似,同样的是需要确定需要读取的从设备的地址。然后主机生成开始信号,再向IIC总线上发送从设备的地址和读数据的指令。从设备接收到地址与自己的吻合后会产生一个应答信号。就这从设备就开始向主机发送主机想要读取的数据,主机正确接收数据后会向从机回复应答信号,当主机想要结束读取操作时,主机会回复一个非应答信号,然后生成停止信号结束数据的读取。 图2-6 2.3. 传输示例 图2-7 如图2-7所示是示波器采集的IIC信号,我们通过自己的观察得到这一段IIC包含的信息,主机向地址为0XA0 的设备写入0X0C。 通过示波器我们可以观察到IIC信号真实的模样, 但是我们也可以体会到示波器在分析数字信号的过程中有很多不便之处。 (1) 示波器分析通道比较少: 一般我们使用的示波器都是双通道,而刚好IIC总线只有两根线组成,但是当我们需要测量的数字信号时由多根线组成的话(比如说spi),用两通道的示波器就不方便我们使用。 (2) 示波器的存储深度比较小:一般来讲示波器的存储深度有限,有与存储深度和采样率采样时长有很大的联系: 存储深度=采样率X显示时间 那么在上面的公式的原则下,示波器的存储深度是一定的,我们想要设置较高的采样率的话就无法显示较长的波形,如图2-8所示,想要的到足够长的波形的话采样率则会不足,如图2-9所示。 图2-8 图2-9低采样率 (3) 没有协议解码功能: 如图2-7所示,示波器抓取到的波形只有光秃秃的波形,我们需要自己将波形放大去仔细辨认才能得到其中的信息。 图2-10逻辑分析仪解码结果 而相比于示波器逻辑分析仪能更好的辅助工程师抓取,识别数字信号。如图2-10所示逻辑分析仪带有解码功能,它可以自动帮助工程师读取出其中数据。逻辑分析仪的通道数量一般都在16个以上,并且在存储深度这方面,逻辑分析仪要远远大于示波器,因此他可以记录很长的数据。 3. 逻辑分析仪准备逻辑分析仪使用详情可参考:https://www.dreamsourcelab.com/doc/DSView_User_Guide.pdf 3.1. 设备连接和状态检查 逻辑分析仪带有一个type-c的接口,使用正确的连接线价格逻辑分析仪接入电脑的USB接口(如果逻辑分析仪是支持USB 3.0 接口的话接入USB 2.0接口会影响其最高速率)。一定要等到逻辑分析仪指示灯显示为绿灯和软件上显示为正确的仪器设备,此时才能正确地操作和使用逻辑分析仪。 图3-1 3.2. 被测信号连接 正常连接好USB后,逻辑分析仪会亮绿灯,再将排线插入逻辑分析仪的检测通道。排线四个通道分为一组,基础版设备4个通道配一根地线,增强版每一个通道都配有地线,由于一般情况下的IIC速率都比较小,所以我们只需要连接两个通道和一根公共地线就行了。 图3-2 4. 波形抓取和协议分析 4.1. 采集设置 采集时长、采样率:关于采样率和采样时长,我们需要明白的一点是他们都和存储深度有关。存储深度=采样率*窗口显示时长 IIC总线的传输速率一般是几百KHZ,我们的传输速率设置为几MHz就行。这里我们采用4MHz的采样率对IIC进行采样。这里不同的采样率下逻辑分析仪的最长采样时长是不一样的,DSView会根据你设置的采样率来计算出最长的采集时长。你可以根据自己的数据量选择相应的采样时长。 图4-1 运行模式和阈值:DSView中逻辑分析仪抓取信号的运行模式有stream和buffer模式。stream模式下不同通道数有不同的最高采样率,而buffer模式下最高采样率是固定的。另外就是由于stream模式采用的是PC的内存所以有很长的采样深度。DSView的电压阈值可以设置的范围是0~5V之间。通常的3.3V数字系统,我们设置阈值电压为1.0V就可以,如果设置太低或者太高会导致信号不精准。 图4-2 这里需要注意的是stream模式下DSView无法进行高级触发,只能进行一些简单触发,如果需要用逻辑分析仪抓取特定的数据的话需要将运行模式调整到buffer模式下。 图4-3 4.2. 触发条件设置和采集 简单触发:简单触发只需要通过设置某个通道的触发条件就可以实现信号的抓取。如图4-4所示我们设置触发条件为当通道1产生边沿信号,当通道1产生上升沿或者下降沿的时候就会触发。 图4-4 采集:DSView能够实现立即和正常捕获。立即捕获可以理解为立即抓取当前的IIC信号,不会顾及触发条件。当你的IIC信号比较短,并且你设置的采样时长比较短时可能会抓取不到波形。 正常捕获只需要点击开始按钮(或者按键盘S键)就可以进入正常捕获模式。正常捕获可以响应触发条件,并且可以设置采集模式。 图4-5 DSView运行单次采集和重复采集。单次采样只允许你采集一次便会停止采集。重复采集是完场一次采集后等待响应的时间间隔后又会重新开始采集并不断这样的循环。如图4-6所示我们可以设置间隔采集的时间,设置范围为1~10S。 图4-6 4.3. 波形查看和测量 缩放:波形的缩放通过鼠标滚轮就能够实现波形的缩放。也可以通过键盘上的左右键来缩放波形。还可以通过鼠标右键框选住波形来将波形细节放大(右键框选只可以放大不能缩小波形)。 图4-7右键框选放大 移动:按住鼠标左键能够实现波形的左右移动。 频率测量:直接将鼠标移动到波形的中央,就可以计算这个信号的频率,周期等信息。 图4-8鼠标检测频率 光标运用:当你的波形比较长窗口无法完全显示的时候,我们可以使用光标标记你想观察的地方然后根据光标实现波形位置的跳转。在波形的上双击就能添加光标,光标的可添加数量不受限制。当你想要实现位置跳转的时候直接鼠标右击光标就会出现相应的序号,然后直接点击序号跳转到相应的光标。 图4-9光标跳转GIF图 4.4. 协议添加和解码分析 我们采集到的IIC总线上面的信号是两条波形图,比较生硬不利于我们了解其中包含的信息。这个时候我们就需要使用到DSView中所包含的解码功能。解码功能可以将IIC协议中包含的内容以直观的形式表现出来,方便我们分析。 步骤一:点击菜单栏上面的解码DSView界面右侧就会出现添加解码协议的窗口。 图4-10 步骤二:在右侧出现的窗口中选择IIC协议,在点击这个加号添加协议。 图4-11 步骤三:点击完加号后下方就会出现一个IIC的协议,点击左侧的设置按钮(齿轮)进入解码设置的窗口。 图4-12 步骤四:首先设置显示的内容,我们可以根据自己的需求设置,然后最重要的是需要将采集IIC的通道与SCL和SDA相对应,从机地址可以显示为包不包含读写位,最后解码区域可以设置为开始到结束,这样采集到的所有数据都会被解码,如果想要解码特定的区域可以添加两条光标,解码区域设置为这两条光标之间。 图4-13 步骤五:结果分析 图4-14 如图4-14所示我们设置了解码区间为光标1和光标2之间的区域。这一段IIC时序表达的意思就是从地址为1010000的设备之中的0X15的内部地址当中读取数据,读出的数据位0X54。 5.进阶分析 5.1.采集技巧 循环采集:当你遇到需要观察一个特定的信号时采用单次可能会不方便操作。比如不同按键代表不同的信息,我们可以设置循环采集抓取按键的信息。 图5-1 触发定位:如果我们想观察一段完整的波形,可以将触发位置尽量调到最前(1%)或最后(90%)。这样就能观察到触发前和触发后的波形,如图5-3所示。 图5-2 图5-3 5.2.高级触发 DSView中可以使用高级触发来分析传输的内容。 步骤一:首先需要在触发设置里面选择高级触发选项。 图5-4 步骤二:设置触发位置, 触发位置可以根据自己的需要设置在任意位置。触发的位置决定了你要抓取的信号位于你抓取的全部信号的百分比位置。设置时只需要填入数值或者用鼠标拖动位置条。 图5-5 步骤三:选择串行触发,IIC是一种串行的通信方式,所以我们选择的是串行触发,上面的总触发等级只在多级触发的时候使用。 图5-6 步骤四:设置开始停止条件、时钟设置。 首先需要将通道与你测试的IIC线对应,例如通道0对应的是SCL,通道1对应的是SDA。IIC的开始条件为当SCL为高电平时SDA产生一个下降沿,停止条件为当SCL为高电平时SDA产生一个低电平,所以开始条件和停止条件设置为图5-7左侧所示。时钟设置用来定义时钟采样的时刻,一般情况我们可以设置为上升沿采样。 图5-7 步骤五:设置数据通道、数据位宽、数据值 数据通道指的是SDA连接的是哪个通道,这里我们连接的是1通道。由于IIC协议中每传送1字节数据就需要一个应答位,所以数据位宽设置为9。例如我设置在出现0X57这个数据时触发,设置的数据值应该为010101110,最后一位为应答位。 当然如果我们不需要抓取特定的数据时,数据值、数据位宽、数据通道三项可以不用设置。 图5-8 步骤六:查看数据 逻辑分析仪抓取的结果会显示在主界面上面,触发的位置就是我们想要获得的数据的位置。 我们还可以通过列表查询的方式来来抓取的这一串数据中的位置。如图5-10所示,输入要查询的数据再点击该数据就能在主界面中快速定位该数据。 图5-9 图5-10 5.3.Stack高阶协议解析 稍微复杂一点的协议都会采用分层的结构,例如使用IIC对EEPROM的读写操作,其协议是基于传统的IIC,但是在这其中还包含了更多的含义。 我们以对EEPROM操作展示Stack高阶协议解析过程 操作步骤: (1)选择“1:IIC”。IIC协议添加“1:IIC”不能添加“0:IIC”,“0:IIC”不能显示多层协议。 图5-11 (2)点击“多层协议” 图5-12 (3)添加对应的EEPROM协议 图5-13 (4) 得到结果 从结果可以看出,这5层的解码将IIC协议通讯的从每一位到整体的意义描述的很详细。 图5-14

    时间:2020-06-01 关键词: 梦源科技 iic-bus iic

  • iic通信协议是什么

    iic通信协议是什么

      iic通信协议是什么   IIC协议是二线制,信号线包含SDA和SCL,且信号线是双向的,开路结构,需要通过上拉电阻到VCC,具体的电阻值影响的是信号反应速度和驱动能力。   首先,IIC通信与UART,还有SPI统称为串行接口通信,不过它们之间还是有区别的,如UART的负电平逻辑,还有UART通信不需要时钟,只需要特定的波特率即可,SPI与IIC都可以有一个主机,多个从机的情况,不过IIC适用于短距离传输,如片间通信,摄像头的配置等场景。   要搞定IIC首先来看IIC的硬件接口:      如图所示,我们知道IIC一个主机可以悬挂多个从机,所以地址线A2,A1,A0 可以实行片选的功能,那么WP这个引脚的功能就是当WP悬空或者接地的时候,表示这时的EEPROM既可以读,也可以写,当WP接电源时,则只可以读而不能写。   SCL与SDL这两个引脚,必须上拉,否则驱动能力不够,无法进行正常的IIC通信。   OK,硬件接口已经介绍清楚了,那么我们现在开始来看协议了。   首先IIC分为字节读写和页面读写,首先来看字节读写的协议:      如上图所示,如果我们要向EEPROM中写入一个字节的数据,得有如下几个步骤:   1.开始信号——在SCLK的高电平器件,拉低SDA的信号(由1 变为0)。   2.控制字节——即器件地址,就是你操作那一块EEPROM。   3.ACK信号——由从机发出,主机为接收,所以在此阶段,sda_link必须置为0,即为读取这个应答信号,所以在SCLK的高点平期间。   4.字节地址——即某一块EEPROM里面的哪一个地址。   5.ACK信号——与上述相同。   6.数据信号——即你往某个地址里面写入的8位数据。   7.ACK信号——上述相同。   8.结束信号——在SCLK的高电平期间,拉高SDA信号,表示通信结束。   再来看读的时序:      由上图可看出读时序的前面处理方式与写相同,不同的时在第三个ACK信号来了之后,如果是读,那么会又有一个起始信号,紧接着读器件地址,然后应答,再然后读数据,再然后在SCLK的低电平期间发送一个NO ACK信号,要记住这个信号由主机发出,然后紧接着一个结束信号。   由上述读写时序我们可知,通信的起始均在SCLK的高电平期间发生跳变,这就据定了我们其他信号跳变均在SCLK的下降沿,SCLK高电平期间数据稳定,适用于读(即低电平改变数据,高电平采集数据)。   具体过程如下:   首先板子上电来个初始化需要来个延时,具体多少用计数器自己搞定。   代码如下:   reg [6:0] hadware_initial_delay;   wire hadware_iniTIal_delay_done;   always@(posedge clk or negedge rst_n)   if(!rst_n)   hadware_iniTIal_delay《=7’d0;   else   if(hadware_iniTIal_delay《=7’d49)   hadware_iniTIal_delay《=hadware_initial_delay+1;else   hadware_initial_delay《=hadware_initial_delay;assign hadware_initial_delay_done=(hadware_initial_delay==7’d50)?1’b1:1’b0;OK,我们要知道IIC的速率一般就几百KH而我们的系统时钟为50M,所以需要分频:   代码如下:   reg [8:0] sclk_cnt;   always@(posedge clk or negedge rst_n)   if(!rst_n)   sclk_cnt《=9’d0;   else   if(hadware_initial_delay_done)   begin   if(sclk_cnt《9’d499)   sclk_cnt《=sclk_cnt+1;   else   sclk_cnt《=0;   end   assign sclk=(sclk_cnt《=9’d249)?1’b1:1’b0;OK,我们知道SCLK高电平期间采集数据,低电平期间改变数据,那么当然,这个“期间”肯定时时钟沿中间最好啦,毕竟更容易满足建立时间与保持时间,很稳定的。   具体代码如下:   wire sclk_posedge_middle=(sclk_cnt==9’d124)?1’b1:1’b0;wire sclk_negedge_middle=(sclk_cnt==9’d374)?1’b1:1’b0;OK,读写定义了那么多个过程,当然需要状态机来搞定啦,定义变量如下:   parameter IDLE = 4’d0 ;   parameter START1 = 4’d1 ;   parameter ADD1 = 4’d2 ;   parameter ACK1 = 4’d3 ;   parameter ADD2 = 4’d4 ;   parameter ACK2 = 4’d5 ;   parameter DATA = 4’d6 ;   parameter ACK3 = 4’d7 ;   parameter STOP1 = 4’d8 ;   parameter START2 = 4’d9 ;   parameter ADD3 = 4’d10;   parameter ACK4 = 4’d11;   parameter DATA_READ = 4’d12;   parameter NO_ACK = 4’d13;   parameter STOP2 = 4’d14;   OK,再来个宏定义,假设写入是这几个地址,这几个数据。   define DEVICE_READ 8‘b1010_0001   define DEVICE_WRITE 8’b1010_0000   define WRITE_DATA 8’b0001_0001   define BYTE_ADDR 8’b0000_0011   SDA双向端口,这个记住,一般这样搞;   reg sda_link;   reg sda_out_r;   assign sda=sda_link?sda_out_r:1’bz;   当作为输出时,对吧,使sda_link拉高,作为输入时,输入高阻。   各过程如下:   reg [3:0] current_state;   //reg [3:0] next_state;   reg [7:0] db_r;   reg [3:0] num;   reg [7:0] data_out_reg;   always@(posedge clk or negedge rst_n)   if(!rst_n)   begin   sda_link《=0;   db_r《=0;   num《=0;   current_state《=IDLE;   sda_out_r《=0;   data_out_reg《=8’b0;   end   else   begin   case(current_state)   IDLE:begin   sda_out_r《=1;   sda_link《=1;   if(!sw1_r||!sw2_r)   current_state《=START1;   else   current_state《=IDLE;   end   START1:if(sclk_posedge_middle)   begin   sda_out_r《=0;   db_r《=`DEVICE_WRITE;   current_state《=ADD1;   end   else   current_state《=START1;   ADD1 :   if(sclk_negedge_middle)   begin   if(num==4‘d8)   begin   sda_link《=0;   num《=0;   current_state《=ACK1;   sda_out_r《=1;   end   else   begin   current_state《=ADD1;   sda_out_r《=db_r[7-num];   num《=num+1;   end   end   else   current_state《=ADD1;   ACK1:   if(sclk_posedge_middle)   // begin   // if(!sda)   // begin   begin // */current_state《=ADD2;   db_r《=`BYTE_ADDR;   end   else   current_state《=ACK1;   ADD2:begin   sda_link《=1;   if(sclk_negedge_middle)begin   if(num==4’d8)   begin   sda_link《=0;   current_state《=ACK2;   num《=4‘d0;   sda_out_r《=1;   end   else   begin   num《=num+1;   current_state《=ADD2;   sda_out_r《=db_r[7-num];   end   end   else   current_state《=ADD2;   end   ACK2:   if(sclk_posedge_middle)   ////begin   //if(!sda)   begin   begin   if(!sw1_r)   begin   db_r《=`WRITE_DATA;   current_state《=DATA;   end   else   if(!sw2_r)   begin   current_state《=START2;   sda_out_r《=1;   end   end   else   current_state《=ACK2;   DATA: begin   sda_link《=1;   if(sclk_negedge_middle)   begin   if(num==4’d8)   begin   num《=4‘d0;   current_state《=ACK3;   sda_out_r《=1;   sda_link《=0;   end   else   begin   num《=num+1;   current_state《=DATA;   sda_out_r《=db_r[7-num];   end   end   else   current_state《=DATA;   end   ACK3: if(sclk_posedge_middle)   // begin   // if(!sda)   current_state《=STOP1;   // end   STOP1:   begin   sda_link《=1;   sda_out_r《=0;   if(sclk_posedge_middle)   begin   sda_out_r《=1;   if(sw1_r)   // 你要是不等它松开才恢复初始状态,那么你一旦恢复初始状态SW1_r就为低电平,他又开始写了,所以为了避免重复写入数据。   current_state《=IDLE;   else   current_state《=STOP1;   end   else   current_state《=STOP1;   end   START2:begin   sda_link《=1;   if(sclk_posedge_middle)   begin   sda_out_r《=0;   sda_link《=1;   db_r《=`DEVICE_READ;   current_state《=ADD3 ;   end   end   ADD3: begin   if(sclk_negedge_middle)   begin   if(num==4’d8)   begin   num《=0;   sda_link《=0;   sda_out_r《=1;   current_state《=ACK4;   end   else   begin   num《=num+1;   sda_out_r《=db_r[7-num];   current_state《=ADD3;   end   end   else   current_state《=ADD3;   end   ACK4:   if(sclk_posedge_middle)   // begin   // if(!sda)   current_state《=DATA_READ;   else   current_state《=ACK4;   // end   DATA_READ:   begin   sda_link《=0;   if(sclk_posedge_middle)   begin   if(num==4‘d8)   begin   sda_link《=1;   sda_out_r《=1;   current_state《=NO_ACK;   num《=4’d0;   end   else   begin   num《=num+1;   current_state《=DATA_READ;   data_out_reg[7-num]《=sda;   end   end   end   NO_ACK:   if(sclk_negedge_middle)   begin   sda_out_r《=1;   current_state《=STOP2;   end   else   current_state《=NO_ACK;   STOP2:begin   sda_out_r《=0;   sda_link《=1;   if(sclk_posedge_middle)   begin   sda_out_r《=1;   current_state《=IDLE;   end   else   current_state《=STOP2;   end   default:current_state《=IDLE;   endcase   end   assign data_out=data_out_reg;   endmodule   仿真结果如下:   

    时间:2020-05-25 关键词: 通信协议 iic

  • IIC和SPI总线协议

    IIC和SPI总线协议

    IICvs SPI         现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影。原因是这两种通信协议非常适合近距离低速芯片间通信。Philips(for IIC)和Motorola(for SPI) 出于不同背景和市场需求制定了这两种标准通信协议。         IIC 开发于1982年,当时是为了给电视机内的CPU和外围芯片提供更简易的互联方式。电视机是最早的嵌入式系统之一,而最初的嵌入系统是使用内存映射(memory-mapped I/O)的方式来互联微控制器和外围设备的。要实现内存映射,设备必须并联入微控制器的数据线和地址线,这种方式在连接多个外设时需大量线路和额外地址解码芯片,很不方便并且成本高。         为了节省微控制器的引脚和和额外的逻辑芯片,使印刷电路板更简单,成本更低,位于荷兰的Philips实验室开发了 ‘Inter-Integrated Circuit’,IIC 或 IIC ,一种只使用二根线接连所有外围芯片的总线协议。最初的标准定义总线速度为100kbps。经历几次修订,主要是1995年的400kbps,1998的3.4Mbps。         有迹象表明,SPI总线首次推出是在1979年,Motorola公司将SPI总线集成在他们第一支改自68000微处理器的微控制器芯片上。SPI总线是微控制器四线的外部总线(相对于内部总线)。与IIC不同,SPI没有明文标准,只是一种事实标准,对通信操作的实现只作一般的抽象描述,芯片厂商与驱动开发者通过data sheets和application notes沟通实现上的细节。SPI     对于有经验的数字电子工程师来说,用SPI互联两支数字设备是相当直观的。SPI是种四根信号线协议(如图):§ SCLK: Serial Clock (output from master);§ MOSI; SIMO: Master Output, Slave Input(output from master);§ MISO; SOMI: Master Input, Slave Output(output from slave);§ SS: Slave Select (active low, outputfrom master).        SPI是[单主设备( single-master )]通信协议,这意味着总线中的只有一支中心设备能发起通信。当SPI主设备想读/写[从设备]时,它首先拉低[从设备]对应的SS线(SS是低电平有效),接着开始发送工作脉冲到时钟线上,在相应的脉冲时间上,[主设备]把信号发到MOSI实现“写”,同时可对MISO采样而实现“读”,如下图:         SPI有四种操作模式——模式0、模式1、模式2和模式3,它们的区别是定义了在时钟脉冲的哪条边沿转换(toggles)输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低)。每种模式由一对参数刻画,它们称为时钟极(clock polarity)CPOL与时钟期(clock phase)CPHA。[主从设备]必须使用相同的工作参数——SCLK、CPOL 和 CPHA,才能正常工作。如果有多个[从设备],并且它们使用了不同的工作参数,那么[主设备]必须在读写不同[从设备]间重新配置这些参数。以上SPI总线协议的主要内容。SPI不规定最大传输速率,没有地址方案;SPI也没规定通信应答机制,没有规定流控制规则。事实上,SPI[主设备]甚至并不知道指定的[从设备]是否存在。这些通信控制都得通过SPI协议以外自行实现。例如,要用SPI连接一支[命令-响应控制型]解码芯片,则必须在SPI的基础上实现更高级的通信协议。SPI并不关心物理接口的电气特性,例如信号的标准电压。在最初,大多数SPI应用都是使用间断性时钟脉冲和以字节为单位传输数据的,但现在有很多变种实现了连续性时间脉冲和任意长度的数据帧。IIC         与SPI的单主设备不同,IIC 是多主设备的总线,IIC没有物理的芯片选择信号线,没有仲裁逻辑电路,只使用两条信号线—— ‘serial data’ (SDA) 和 ‘serial clock’ (SCL)。IIC协议规定:§ 第一,每一支IIC设备都有一个唯一的七位设备地址;§ 第二,数据帧大小为8位的字节;§ 第三,数据(帧)中的某些数据位用于控制通信的开始、停止、方向(读写)和应答机制。IIC 数据传输速率有标准模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps),另外一些变种实现了低速模式(10 kbps)和快速+模式(1 Mbps)。物理实现上,IIC 总线由两根信号线和一根地线组成。两根信号线都是双向传输的,参考下图。IIC协议标准规定发起通信的设备称为主设备,主设备发起一次通信后,其它设备均为从设备。IIC 通信过程大概如下。首先,主设备发一个START信号,这个信号就像对所有其它设备喊:请大家注意!然后其它设备开始监听总线以准备接收数据。接着,主设备发送一个7位设备地址加一位的读写操作的数据帧。当所设备接收数据后,比对地址自己是否目标设备。如果比对不符,设备进入等待状态,等待STOP信号的来临;如果比对相符,设备会发送一个应答信号——ACKNOWLEDGE作回应。当主设备收到应答后便开始传送或接收数据。数据帧大小为8位,尾随一位的应答信号。主设备发送数据,从设备应答;相反主设备接数据,主设备应答。当数据传送完毕,主设备发送一个STOP信号,向其它设备宣告释放总线,其它设备回到初始状态。基于IIC总线的物理结构,总线上的START和STOP信号必定是唯一的。另外,IIC总线标准规定SDA线的数据转换必须在SCL线的低电平期,在SCL线的高电平期,SDA线的上数据是稳定的。在物理实现上,SCL线和SDA线都是漏极开路(open-drain),通过上拉电阻外加一个电压源。当把线路接地时,线路为逻辑0,当释放线路,线路空闲时,线路为逻辑1。基于这些特性,IIC设备对总线的操作仅有“把线路接地”——输出逻辑0。IIC总线设计只使用了两条线,但相当优雅地实现任意数目设备间无缝通信,堪称完美。我们设想一下,如果有两支设备同时向SCL线和SDA线发送信息会出现什么情况。基于IIC总线的设计,线路上不可能出现电平冲突现象。如果一支设备发送逻辑0,其它发送逻辑1,那么线路看到的只有逻辑0。也就是说,如果出现电平冲突,发送逻辑0的始终是“赢家”。总线的物理结构亦允许主设备在往总线写数据的同时读取数据。这样,任何设备都可以检测冲突的发生。当两支主设备竞争总线的时候,“赢家”并不知道竞争的发生,只有“输家”发现了冲突——当它写一个逻辑1,却读到0时——而退出竞争。10位设备地址         任何IIC设备都有一个7位地址,理论上,现实中只能有127种不同的IIC设备。实际上,已有IIC的设备种类远远多于这个限制,在一条总线上出现相同的地址的IIC设备的概率相当高。为了突破这个限制,很多设备使用了双重地址——7位地址加引脚地址(external configuration pins)。IIC 标准也预知了这种限制,提出10位的地址方案。10位的地址方案对 IIC协议的影响有两点:§ 第一,地址帧为两个字节长,原来的是一个字节;§ 第二,第一个字节前五位最高有效位用作10位地址标识,约定是“11110”。除了10位地址标识,标准还预留了一些地址码用作其它用途,如下表:时钟拉伸        在 IIC 通信中,主设备决定了时钟速度。因为时钟脉冲信号是由主设备显式发出的。但是,当从设备没办法跟上主设备的速度时,从设备需要一种机制来请求主设备慢一点。这种机制称为时钟拉伸,而基于I²C结构的特殊性,这种机制得到实现。当从设备需要降低传输的速度的时候,它可以按下时钟线,逼迫主设备进入等待状态,直到从设备释放时钟线,通信才继续。高速模式       原理上讲,使用上拉电阻来设置逻辑1会限制总线的最大传输速度。而速度是限制总线应用的因素之一。这也说明为什么要引入高速模式(3.4 Mbps)。在发起一次高速模式传输前,主设备必须先在低速的模式下(例如快速模式)发出特定的“High Speed Master”信号。为缩短信号的周期和提高总线速度,高速模式必须使用额外的I/O缓冲区。另外,总线仲裁在高速模式下可屏蔽掉。更多的信息请参与总线标准文档。IIC vs SPI: 哪位是赢家?         我们来对比一下IIC 和 SPI的一些关键点:第一,总线拓扑结构/信号路由/硬件资源耗费       IIC 只需两根信号线,而标准SPI至少四根信号,如果有多个从设备,信号需要更多。一些SPI变种虽然只使用三根线——SCLK, SS和双向的MISO/MOSI,但SS线还是要和从设备一对一根。另外,如果SPI要实现多主设备结构,总线系统需额外的逻辑和线路。用IIC 构建系统总线唯一的问题是有限的7位地址空间,但这个问题新标准已经解决——使用10位地址。从第一点上看,IIC是明显的大赢家。第二,数据吞吐/传输速度          如果应用中必须使用高速数据传输,那么SPI是必然的选择。因为SPI是全双工,IIC 的不是。SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps。IIC 最高的速度也就快速+模式(1 Mbps)和高速模式(3.4 Mbps),后面的模式还需要额外的I/O缓冲区,还并不是总是容易实现的。第三,优雅性         IIC 常被称更优雅于SPI。公正的说,我们更倾向于认为两者同等优雅和健壮。IIC的优雅在于它的特色——用很轻盈的架构实现了多主设备仲裁和设备路由。但是对使用的工程师来讲,理解总线结构更费劲,而且总线的性能不高。SPI的优点在于它的结构相当的直观简单,容易实现,并且有很好扩展性。SPI的简单性不足称其优雅,因为要用SPI搭建一个有用的通信平台,还需要在SPI之上构建特定的通信协议软件。也就是说要想获得SPI特有而IIC没有的特性——高速性能,工程师们需要付出更多的劳动。另外,这种自定的工作是完全自由的,这也说明为什么SPI没有官方标准。IIC和SPI都对低速设备通信提供了很好的支持,不过,SPI适合数据流应用,而IIC更适合“字节设备”的多主设备应用。小结       在数字通信协议簇中,IIC和SPI常称为“小”协议,相对Ethernet, USB, SATA, PCI-Express等传输速度达数百上千兆字节每秒的总线。但是,我们不能忘记的是各种总线的用途是什么。“大”协议是用于系统外的整个系统之间通信的,“小”协议是用于系统内各芯片间的通信,没有迹象表明“大”协议有必要取代“小”协议。IIC和SPI的存在和流行体现了“够用就好”的哲学。回应文首,IIC和SPI如此的流行,它是任何一位嵌入式工程师必备的工具。

    时间:2019-06-11 关键词: 总线协议 iic

  • IIC读写AT24Cxx (S3C2440)

    IIC(Inter-Integrated Circuit,I2C)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微处理器及其外围设备,它的最主要优点是简单和有效。它只需要数据线SDA和时钟线SCL,就能够实现CPU与被控IC之间、IC与IC之间进行双向传送。 s3c2440内部有一个IIC总线接口,因此为我们连接带有IIC通信模块的外围设备提供了便利。它具有四种操作模式:主设备发送模式、主设备接收模式、从设备发送模式和从设备接收模式。在这里我们只把s3c2440当做IIC总线的主设备来使用,因此只介绍前两种操作模式。在主设备发送模式下,它的工作流程为:首先配置IIC模式,然后把从设备地址写入接收发送数据移位寄存器IICDS中,再把0xF0写入控制状态寄存器IICSTAT中,这时等待从设备发送应答信号,如果想要继续发送数据,那么在接收到应答信号后,再把待发送的数据写入寄存器IICDS中,清除中断标志后,再次等待应答信号;如果不想再发送数据了,那么把0x90写入寄存器IICSTAT中,清除中断标志并等待停止条件后,即完成了一次主设备的发送。在主设备接收模式下,它的工作流程为:首先配置IIC模式,然后把从设备地址写入接收发送数据移位寄存器IICDS中,再把0xB0写入控制状态寄存器IICSTAT中,这时等待从设备发送应答信号,如果想要接收数据,那么在应答信号后,读取寄存器IICDS,清除中断标志;如果不想接收数据了,那么就向寄存器IICSTAT写入0x90,清除中断标志并等待停止条件后,即完成了一次主设备的接收。在完成上述两个模式时,主要用到了控制寄存器IICCON、控制状态寄存器IICSTAT和发送接收数据移位寄存器IICDS。由于我们只把s3c2440当做主设备来用,并且系统的IIC总线上只有这么一个主设备,因此用来设置从设备地址的地址寄存器IICADD,和用于仲裁总线的多主设备线路控制寄存器IICLC都无需配置。寄存器IICCON的第6位和低4位用于设置IIC的时钟频率,因为IIC的时钟线SCL都是由主设备提供的。s3c2440的IIC时钟源为PCLK,当系统的PCLK为50MHz,而从设备最高需要100kHz时,可以将IICCON的第6位置1,IICCON的低4位全为0即可。寄存器IICCON的第7位用于设置是否发出应答信号,第5位用于是否使能发送和接收中断,第4位用于中断的标志,当接收或发送数据后一定要对该位进行清零,以清除中断标志。寄存器IICSTAT的高2位用于设置是哪种操作模式,当向第5位写0或写1时,则表示结束IIC或开始IIC通讯,第4位用于是否使能接收/发送数据。 由于通讯是双方的事情,在了解了主设备的操作模式后,还要清楚从设备的运行机制,两者要达到完美地结合,才能实现彼此的通讯。在这里,从设备是EEPROM——AT24C02A,要想让s3c2440能够正确地对AT24C02A读写,就必须让s3c2440的时序完全按照AT24C02A的时序。AT24C02A的写操作有两种模式:字节写和页写。字节写是先接收带有写命令的设备地址信息,如果符合就应答,再接收设备内存地址信息,发出应答后,再接收要写入的数据,这样就完成了字节写过程。页写与字节写的区别就是,页写可以一次写多个数据,而字节写只能一次写一个数据。但由于AT24C02A的一页才8个字节,所以页写也最多写8个数据,而且只能在该页内写,不会发生一次页写同时写两页的情况。AT24C02A的读操作有三种模式:当前地址读、随机读和序列读。当前地址读是只能读取当前地址内的数据,它的时序是先接收带有读命令的设备地址信息,如果符合就应答,然后发送当前地址内的数据,在没有接收从主设备发来的应答信号的情况下终止该次操作。随机读的时序是,连续接收带有写命令的设备地址信息和设备内存地址信息,然后主设备重新开启IIC通信,AT24C02A再次接收到带有读命令的设备地址信息,在发出应答信号以后,发送该内存地址的数据,在没有接收到任何应答信号的情况下结束该次通信。当前地址读和随机读一次都只能读取一个数据,而序列读一次可以读取若干个数据,它的时序就是在当前地址读或随机读发出数据后,接收到了应答信号,那么AT24C02A会把下一个内存地址中的数据送出,除非AT24C02A接收不到任何应答信号,否则它会一直把下一个内存地址中的数据送出。序列读没有一页8个字节的限制。 在介绍完了s3c2440和AT24C02A的IIC通讯方式后,我们就可以写程序了。在这里,我们用UART来实现PC机对AT24C02A的读写。UART的通讯协议是,PC机先发送命令字节:0xC0表示要向AT24C02A写数据,0xC1表示要读取AT24C02A的数据,在命令字节后,紧跟着的是设备内存地址和写入或读取的字节数。如果是要写EEPROM数据,则在这三个字节后是要写入的数据内容。在UART通讯完毕后,s3c2440会根据命令的不同,写入或读取AT24C02A,如果是读取EEPROM,则s3c2440还会利用UART把读取到的数据上传到PC机。在这个程序中,我们只开启了UART的接收中断,而没有开启发送中断,即让s3c2440主动去完成发送任务。并且在与AT24C02A操作中,我们使用的是页写和序列读的模式,这样可以最大程度的完成一次读或写操作,而且我们所编写的页写和序列读子程度也同样可以实现字节写和随机读的模式。在这里我们限制一次读或写的数据量最多为8个字节。 以上转自: 作者:zhaocj 来源:CSDN 原文:https://blog.csdn.net/zhaocj/article/details/5477152 版权声明:本文为博主原创文章,转载请附上博文链接! 在发送模式: 1.往寄存器IICDS寄存器放入一个val值。 2.发完,产生中断,并且会把 SCL拉低。 3.在中断程序里,判断状态,然后往IICDS里面写入下一个数据,一旦写入下一个数据IIC继续操作,若再次发完,就会再次产生中断。 在接受模式: 1.我的程序发起传输,接受数据。 2.接收到数据之后,产生中断,SCL被拉低。 3.中断程序里,判断数据是否要继续接受等,如果还有继续接受的话,再次设置,设置好之后读IICDS寄存器,一但读出来IIC。 继续接受下一个数据,收到新数据之后,又会产生一个中断(就是这样循环操作)。 按照S3C2440的IIC传输流程图 和AT24Cxx的传送模式 ARM每传完一个数据就进入一次中断,所以,在中断服务程序中清中断。下次传输时还要清除IICCON的bit4的中断标志位。 /* ### i2c_drive文件 ### 包括,初始化i2c,读i2c,写i2c,i2c中断处理函数 */ #include "i2c_drive.h" #define AT24Cxx_ADDR 0x50 unsigned char ask = 1; //应答标志位,0表示应答成功 static void delay(unsigned int time) { for (; time>0; time--); } void i2c_init(void) { /* GPE15--IICSDA, GPE14--IICSCL */ GPECON &= ~((3<<28) | (3<<30)); GPECON |= (2<<28) | (2<<30); /* 设置IICCON总线控制寄存器 */ /* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode * [6] : 时钟源, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512 * [5] : 1-enable interrupt * [4] : 读出为1时表示中断发生了, 写入0来清除并恢复I2C操作 * [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1). * Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1) */ IICCON = 0xe0; IICSTAT = 0x10; INTMSK &= ~(1<<27); } void i2c_interrupt(void) { /* 清除中断标志位 */ SRCPND &= ~(1<<27); INTPND &= ~(1<<27); ask = 0; } void write_at24cxx(unsigned char addr, unsigned char *buffer, int sizeofdate) { int i; ask = 1; IICDS = AT24Cxx_ADDR; IICCON &= ~(1<<4); //清中断 IICSTAT = 0xf0; //配置为主机发送模式,发出S信号, //IICDS寄存器中的数据自动发出,使能串行发送接收功能 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 ask = 1; IICDS = addr; //发送设备内存地址 IICCON &= ~(1<<4); //清中断 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 for (i=0; i<="" p=""> { ask = 1; IICDS = *(buffer+i); IICCON &= ~(1<<4); //清中断 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 } IICSTAT = 0xd0; //发送P IICCON = 0xe0; delay(100); } void read_at24cxx(unsigned char addr, unsigned char *buffer, int sizeofdate) { int i; unsigned char temp; ask = 1; IICDS = AT24Cxx_ADDR; IICCON &= ~(1<<4); //清中断 IICSTAT = 0xf0; //配置为主机发送模式,发出S信号, //IICDS寄存器中的数据自动发出,使能串行发送接收功能 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 ask = 1; IICDS = addr; //发送设备内存地址 IICCON &= ~(1<<4); //清中断 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 ask = 1; IICDS = AT24Cxx_ADDR; //发送设备内存地址 IICCON &= ~(1<<4); //清中断 IICSTAT = 0xb0; //主设备接收模式 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 ask = 1; temp = IICDS; IICCON &= ~(1<<4); //清中断 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 for (i=0; i<="" p=""> { ask = 1; if (i == sizeofdate-1) IICCON &= ~(1<<7); //ACK信号禁止 *(buffer+i) = IICDS; IICCON &= ~(1<<4); //清中断 while (ask == 1) //等待从设备应答 delay(100); //一旦应答,进入中断使得ASK=0,则跳出该死循环 } IICSTAT = 0x90; //发送P IICCON = 0xe0; delay(100); }

    时间:2019-05-17 关键词: 数据 寄存器 iic

  • IIC的总线应用

    LPC2124有一个标准的I2C总线接口,可配置为主机或从机,总线时钟速率可调整,最高可支持400KHZ总线速率。使用I2C总线时,要将相应得引脚设置连接SCL和SDA,并且总线上要上拉电阻,阻值为1~10KΩ,依据所需要的总线速率而定。总线速率越高,电阻阻值应该越小。I2C总线可接标准I2C接口器件,如串行EEPROM、RAM、LCD、时钟芯片以及音调发生器。根据方向位(R/W)状态的不同,I2C总线上存在以下两种类型的数据传输:⑴从主发送器向从接收器发送数据。主机发送得第一个字节是从机地址,接下来是数据字节流。从机每接收到一个字节返回一个应答位。⑵从发送器向主接收器发送数据。第一个字节(从地址)从主机发送。从机返回一个应答位。接下来从机向主机发送数据字节。主机每接收一个字节返回一个应答位。接收完最后一个字节,主机返回一个“非应答位”。主器件产生所有串行时钟脉冲和起始以及停止条件。出现停止条件或重复的起始条件时传输结束。由于重复的起始条件同时是下一个串行发送的开始,因此I2C总线不会被释放。LPC2124的I2C是字节方式的I2C接口,只要把一字节数据写入I2C数据寄存器I2DAT后,即可由I2C接口自动完成所有的数据位发送。它有4种操作模式:主发送器模式、主接收器模式、从发送器模式和从接收器模式。⑴主发送器模式该模式中,数据从主机发送到从机。在进入主发送器模式之前,I2CONSET(I2C控制置位寄存器)必须按如下设置进行初始化:I2CONSET --- I2EN STA STO SI AA --- --- -- 1 0 0 0 0 -- --说明如下: I2EN=1,使能I2C接口; AA=0,不产生应答信号,即不允许进入从机模式; SI=0,I2C中断标志为0; STO=0,停止标志为0; STA=0,起始标志为0。在该模式下,数据方向位(R/W)应为0表示执行写操作。因此第一个发送的字节为从地址(7位)和写方向位。数据的发送每次为8位,每发送完一字节,都接收到一个由从机返回的应答位。该模式的数据发送操作步骤如下:① 通过软件置位STA,进入I2C主发送器模式,I2C逻辑在总线空闲后立即发送一个起始条件。② 当发送完起始条件后,SI位置位。此时I2STAT(状态寄存器)中的状态代码为08H,该状态代码用于中断服务程序的处理。③ 把从地址和写方向位装入I2DAT(数据寄存器),然后清零SI位(向I2CONCLR寄存器中得SIC位写入1可清零SI),开始发送从地址和写方向位。④ 当从地址和写方向位已发送且接收到应答位后,SI位再次置位(可能的状态代码为18H,20H或38H)。⑤ 当状态码为18H时,表明从机已应答,则可以将数据装入I2DAT,然后清零SI位,开始发送数据。⑥ 当正确发送数据,SI位再次置位(可能的状态码为28H,30H)。此时可以再次发送数据或者置位STO结束总线。⑵主接收器模式在该模式下,主机所接收的数据字节来自从发送器,数据方向位应该为1表示执行读操作。该模式的数据接收操作步骤如下:① 通过软件置位STA,进入I2C主发送器模式,I2C逻辑在总线空闲后立即发送一个起始条件。② 当发送完起始条件后,SI位置位。此时I2STAT(状态寄存器)中的状态代码为08H,该状态代码用于中断服务程序的处理。③ 把从地址和读方向位装入I2DAT(数据寄存器),然后清零SI位,开始发送从地址和读方向位。④ 当从地址和读方向位已发送且接收到应答位后,SI位再次置位(可能的状态代码为38H,40H或48H)。⑤ 当状态码为40H时,表明从机已应答。设置AA位,用来控制接收到数据后是产生应答信号还是产生非应答信号,然后清零SI位,开始接收数据。⑥ 当正确接收到一个字节数据后,SI位再次置位(可能的状态码为50H或58H)。此时可以再次接收数据或者置位STO结束总线。⑶从接收器模式当配置为I2C从机时,I2C主机可以对它进行读、写操作。要初始化为从机用户必须将从地址写入I2ADR(从地址寄存器),并按如下配置I2CONSET:I2CONSET --- I2EN STA STO SI AA --- --- -- 1 0 0 0 1 -- -- 在该模式下,从主发送器接收数据字节。当主机访问从机时,接收主机发送过来的数据,并产生应答信号。数据方向位应该为0表示写操作。该模式的数据接收操作步骤如下:① 将从地址写入I2ADR并配置I2CONSET完成初始化。等待它被自身的从地址或通用地址寻址。② 在接收到地址和方向位后,SI位置位并可从I2STAT中读出有效的状态代码。③ 根据状态代码执行相应的操作。⑷从发送器模式当主机访问从机时,向主机发送数据,并等待主机的应答信号。数据方向位应该为1表示读操作。使用该模式时,用户程序只需根据各种状态码作出相应的操作。I2C时钟由SCL占空比寄存器控制(I2SCLH、I2SCLL),分别设置SCL的高电平和低电平时间,获得合适的总线时钟频率。寄存器I2CONSET则用来控制I2C总线的模式及总线操作,其位SI为I2C中断标志位,所有总线操作都要依赖于这一标志;同时它又钳住总线,使总线的数据发送/接收得以同步控制。寄存器I2CONCLR则为对应的清零寄存器;I2STAT为I2C状态寄存器,用于指示总线处于哪种状态,以方便控制操作。I2DAT为I2C总线数据寄存器,包含要发送的数据或刚收到的数据。当系统作为从机时,I2ADR从机地址寄存器有效。当总线对此地址进行访问时,将会产生I2C中断。I2C主机基本操作方法:①设置I2C引脚连接;②设置I2C时钟速率(I2SCLH、I2SCLL);③设置为主机,并发送起始信号(I2CONSET的I2EN、STA位为1,AA位为0);④发送从机地址(I2DAT),控制I2CONSET发送;⑤判断总线状态(I2STAT),进行数据传输控制;⑥发送结束信号(I2CONSET)。I2C从机基本操作方法:①设置I2C引脚连接;②设置自身的从机地址(I2ADR);③使能I2C(I2CONSET的I2EN、AA位为1);④判断SI位或者等待I2C中断,等待主机操作;⑤判断总线状态I2STAT,进行数据传输控制。

    时间:2019-01-08 关键词: 总线应用 iic

  • IIC的PCF8563实用时钟程序(iccavr)

    /************************************** PCF8563时钟程序 ** 文 件 名:1602_8563.c ** 版 本:V22.02 ** 主控芯片:Mega16L ** 工作频率:7.3728MHz **************************************/#include #include #include #include #include #include #include //数据简化宏定义#define uchar unsigned char#define uint unsigned int//全局变量定义uchar timer[8]; //时钟数据/*********************************** 蜂鸣器发声函数 ** 函数功能:蜂鸣器发出声响 ** 入 口:n 声次数 ** i 声时长,i*10ms ** 返 回:无 ** 发声频率:固定1KHz 无源 ***********************************/void spk(uchar i,uchar n) {uint a;for(;n;n--) {a=i*10; //计算每声响的长度:MSfor(;a;a--) {delay_us(500); //响声频率为1KHZSPK_TG;delay_us(500);SPK_TG;}SPK_OFF; //响完关闭蜂鸣器delay_ms(100); //每声响之间隔100MS}}/*********************************** 读出数据函数 ***********************************/uchar read_timer(void) {start(); //启动总线waitack(); //等待启动完成if(chkack()!=START)return I2C_ERR; //检查是否启动成功write_tim(SLA_W); //发送写地址write_tim(0x02); //写数据地址start();waitack(); //等待启动完成if(chkack()!=RESTART)return I2C_ERR; //检查是否启动成功write_tim(SLA_R); //发送读地址timer[0]=read_tim()&0x7F; //读出秒数据timer[1]=read_tim()&0x7F; //读出分数据timer[2]=read_tim()&0x3F; //读出时数据timer[3]=read_tim()&0x3F; //读出日数据timer[4]=read_tim()&0x07; //读出周数据 if(timer[4]==0)timer[4]=7;timer[5]=read_tim(); //读出月数据 if((timer[5]&0x80)==0) timer[7]=0x20; //世纪位为0,是21世纪 else timer[7]=0x19; //世纪位不为0,是20世纪timer[5]=timer[5]&0x1F;timer[6]=read_tim(); //读出年数据stop();return I2C_CRR;}/*********************************** 写入时钟函数 ***********************************/uchar write_timer(void) {start(); //启动总线waitack(); //等待启动完成if(chkack()!=START)return I2C_ERR; //检查是否启动成功write_tim(SLA_W); //发送写地址write_tim(0x00); //写数据首地址write_tim(0x20); //写控制/状态寄存器1,暂停计时write_tim(0x00); //写控制/状态寄存器2write_tim(0x00); //写秒数据为0write_tim(timer[1]); //写分数据write_tim(timer[2]); //写时数据write_tim(timer[3]); //写日数据write_tim(timer[4]); //写周数据write_tim(timer[5]); //写月数据write_tim(timer[6]); //写年数据stop();return I2C_CRR;}/*********************************** 启动时钟函数 ***********************************/uchar start_timer(void) {start(); //启动总线waitack(); //等待启动完成if(chkack()!=START)return I2C_ERR; //检查是否启动成功write_tim(SLA_W); //发送写地址write_tim(0x00); //写数据首地址write_tim(0x00); //写控制/状态寄存器1,暂停计时stop();return I2C_CRR;}/*********************************** 将时钟数据转换后在LCD上显示 ***********************************/void timer_lcd(void) {locate(1,4); //写指令:第1行第4列地址lcd_da((timer[7]>>4)+0x30);lcd_da((timer[7]&0x0f)+0x30);lcd_da((timer[6]>>4)+0x30); //显示年lcd_da((timer[6]&0x0f)+0x30);lcd_da('/');lcd_da((timer[5]>>4)+0x30); //显示月lcd_da((timer[5]&0x0f)+0x30);lcd_da('/');lcd_da((timer[3]>>4)+0x30); //显示日lcd_da((timer[3]&0x0f)+0x30);locate(2,2); //写指令:第2行地址 lcd_da((timer[2]>>4)+0x30); //时lcd_da((timer[2]&0x0f)+0x30);lcd_da(':');lcd_da((timer[1]>>4)+0x30); //分lcd_da((timer[1]&0x0f)+0x30);lcd_da(':');lcd_da((timer[0]>>4)+0x30); //秒lcd_da((timer[0]&0x0f)+0x30);lcd_da(20); //时间与星期间留1空格lcd_da('W'); //星期的前导字lcd_da('e');lcd_da('e');lcd_da('k');lcd_da((timer[4]&0x0f)+0x30); //星期数据}//定时器1:每秒从8563中读取4次数据,更新显示void timer1_init(void) //定时器1初始化:250毫秒定时,预分频256 {TCCR1B = 0x00; //停止定时器TCNT1H = 0x8F; //初值高字节TCNT1L = 0x81; //定时初值低字节TCCR1A = 0x00;TCCR1B = 0x03; //启动定时器}#pragma interrupt_handler timer1_ovf_isr:9void timer1_ovf_isr(void) //定时器1中断入口:250MS中断一次 {TCNT1H = 0x8F; //重装初值TCNT1L = 0x81;read_timer(); //读出当前时钟timer_lcd(); //显示数据转换}/*********************************** 调整显示函数1 ***********************************/void set_xs1(uchar i) {lcd_da((timer>>4)+0x30); //显示数据lcd_da((timer&0x0f)+0x30);}/*********************************** 调整显示函数2 ***********************************/void set_xs2(uchar data) {lcd_da((data>>4)+0x30); //显示数据lcd_da((data&0x0f)+0x30);lcd(0x20);}/*********************************** 程序主函数 ***********************************/void main(void) {uchar set_flag=0; //调整与正常工作标志uchar set_time; //调整数据类型标志uchar set_bh=0; //调整变化标志uchar a,a1;port_init();LED_ON; //开LCD背光lcd_init();delay_ms(500);tonghe();delay_ms(2000);Twi_Init();delay_ms(100);CLI(); //关总中断timer1_init();//定时器1初始化MCUCR = 0x00;GICR = 0x00;TIMSK = 0x04; //开放定时器0中断和比较中断SEI(); //开总中断cls();delay_ms(50);while(1) {a=KEY_RD;if(a!=0) {delay_ms(20);a1=KEY_RD;if(a1==a) {switch(a) {case 0x01:spk(10,1);if(set_flag==0) {TCCR1B=0x00;LED_ON;set_flag=1;cls();locate(1,4); //显示“时间调整”xs_lcd("SET:");}else{TCCR1B=0x04; //开启时钟set_flag=0;if(set_bh==1) {write_timer(); //写入新时间set_bh=0;}}break;case 0x02: if(set_flag==1) {spk(10,1);set_time++;locate(2,2); //显示待调整的数据switch(set_time) {case 1: xs_lcd("year:");set_xs1(6); //显示年break;case 2: xs_lcd("muth:");set_xs1(5); //显示月break;case 3: xs_lcd(" day:");set_xs1(3); //显示日break;case 4: xs_lcd("hour:");set_xs1(2); //显示时break;case 5: xs_lcd("mine:");set_xs1(1); //显示分break;case 6: xs_lcd("week:");set_xs1(4); //显示周break;default:set_time=0;break;}}break;case 0x04: if(set_flag==1) {spk(10,1);set_bh=1;locate(2,7); //显示“时间调整”switch(set_time) {case 1: timer[6]-=0x01;if((timer[6]&0x0F)==0x0F)timer[6]&=0xF9;if(timer[6]>0x99)timer[6]=0x99;set_xs1(6); //显示年break;case 2: timer[5]-=0x01;if((timer[5]&0x0F)==0x0F)timer[5]&=0xF9;if(timer[5]==0x00)timer[5]=0x12;set_xs1(5); //显示月break;case 3: timer[3]-=0x01;if((timer[3]&0x0F)==0x0F)timer[3]&=0xF9;if(timer[3]==0x00)timer[3]=0x30;set_xs1(3); //显示日break;case 4: timer[2]-=0x01;if((timer[2]&0x0F)==0x0F)timer[2]&=0xF9;if(timer[2]>0x24)timer[2]=0x23;set_xs1(2); //显示时break;case 5: timer[1]-=0x01;if((timer[1]&0x0F)==0x0F)timer[1]&=0xf9;if(timer[1]>0x59)timer[1]=0x59;set_xs1(1); //显示分break;case 6: timer[4]-=0x01;if(timer[4]>6)timer[4]=0x06;set_xs1(4); //显示周break;default:set_time=0;break;}}elsestart_timer();break;case 0x08: if(set_flag==1) {spk(10,1);set_bh=1;locate(2,7); //显示“时间调整”switch(set_time){case 1:timer[6]+=0x01;if((timer[6]&0x0F)==0x0A)timer[6]=(timer[6]&0xF0)+0x10;if(timer[6]>0x99)timer[6]=0x00;set_xs1(6); //显示年break;case 2:timer[5]+=0x01;if((timer[5]&0x0F)==0x0A)timer[5]=(timer[5]&0xF0)+0x10;if(timer[5]>=0x13)timer[5]=0x01;set_xs1(5); //显示月break;case 3:timer[3]+=0x01;if((timer[3]&0x0F)==0x0A)timer[3]=(timer[3]&0xF0)+0x10;if(timer[3]>=0x31)timer[3]=0x01;set_xs1(3); //显示日break;case 4:timer[2]+=0x01;if((timer[2]&0x0F)==0x0A)timer[2]=(timer[2]&0xF0)+0x10;if(timer[2]>=0x24)timer[2]=0x00;set_xs1(2); //显示时break;case 5:timer[1]+=0x01;if((timer[1]&0x0F)==0x0A)timer[1]=(timer[1]&0xf0)+0x10;if(timer[1]>0x59)timer[1]=0x00;set_xs1(1); //显示分break;case 6:timer[4]+=0x01;if(timer[4]>6)timer[4]=0x00;set_xs1(4); //显示周break;default:set_time=0;break;}}elseLED_OFF; //关背光break;default:break;}}while(a!=0) {a=KEY_RD;delay_ms(10);}}}}

    时间:2019-01-07 关键词: 时钟程序 pcf8563 iccavr iic

  • stm8s开发(八) IIC的使用:IIC主机通信!

    前面讲过两个常用的串口,UART和SPI,这次这次讲解一下另一个常用的串口:IIC(I2C)通信科普IIC:一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。通信过程:主模式时,IIC接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。起始条件和停止条件都是在主模式下由软件控制产生。从模式时,IIC接口能识别它自己的地址(7位或10位)和广播呼叫地址。软件能够控制开启或禁止广播呼叫地址的识别。数据和地址按8位/字节进行传输,高位在前。跟在起始条件后的1或2个字节是地址(7位模式为1个字节,10位模式为2个字节)。地址只在主模式发送。在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。参考下图。详细协议可以参考:http://blog.csdn.net/subkiller/article/details/6854910和SPI差不多,使用IIC无非就一个初始化,一个数据发送,一个数据接收,三大功能。初始化分初始化为主机、从机,不过一般和外部芯片通信单片机都是作为主机。voidIIC_Master_Init(void){CLK_PCKENR1|=0x01;//使能IIC外设时钟PB_DDR&=0xcf;PB_CR1&=0xcf;PB_CR2&=0xcf;I2C_CR1=0x00;//允许时钟延展,禁止广播呼叫,禁止iicI2C_FREQR=0x01;//输入时钟频率8MHzI2C_OARH=0x40;//七位地址模式I2C_OARL=0xa0;//自身地址0xa0I2C_CCRL=0xff;//I2C_CCRH=0x00;//标准模式I2C_TRISER=0x02;I2C_CR1|=0x01;//使能iic外设}这里使用的是STM8S105片上的IIC引脚PB4、PB5。另外就是需要使能IIC的时钟。发送和接收数据这里只给出一个最简单的例子,因为不同的外部芯片的通信方式不一样,不过一般都是:读操作:开始->发送外设地址 -> 开始->发送需要读取的寄存器地址-> 读一个字节-> (可能再读一个字节)->。。。-> 结束写操作:开始->发送外设地址 ->发送需要写入的寄存器地址-> 写一个字节->voidIIC_Write_Byte(u8DeviceAddress,u8Address,u8Data){vu8temp=0;while((I2C_SR3&0x02)!=0);//等待IIC总线空闲IIC_Start();while((I2C_SR1&0x01)==0);//EV5,起始信号已经发送I2C_DR=(DeviceAddress&0xfe);//发送iic从器件物理地址,最低位0,写操作while((I2C_SR1&0x02)==0);//地址已经被发送temp=I2C_SR1;//清除ADDR标志位temp=I2C_SR3;while((I2C_SR1&0x80)==0);//等待发送寄存器为空I2C_DR=Address;//发送要写入的寄存器地址while((I2C_SR1&0x04)==0);//等待发送完成while((I2C_SR1&0x80)==0);//等待发送寄存器为空I2C_DR=Data;//发送要写入的数据while((I2C_SR1&0x04)==0);//等待发送完成temp=I2C_SR1;//清零BTF标志位temp=I2C_DR;IIC_Stop();//发送停止信号}unsignedcharIIC_Read_Byte(u8DeviceAddress,u8Address){vu8temp=0;shortread_data=0;while((I2C_SR3&0x02)!=0);//等待IIC总线空闲I2C_CR2|=0x04;//使能ACKIIC_Start();while((I2C_SR1&0x01)==0);//EV5,起始信号已经发送I2C_DR=(DeviceAddress&0xfe);//发送iic从器件物理地址,最低位0,写操作while((I2C_SR1&0x02)==0);//地址已经被发送temp=I2C_SR1;//清除ADDR标志位temp=I2C_SR3;while((I2C_SR1&0x80)==0);//等待发送寄存器为空I2C_DR=Address;//发送要读取的寄存器地址while((I2C_SR1&0x04)==0);//等待数据发送完成IIC_Start();while((I2C_SR1&0x01)==0);//EV5,起始信号已经发送I2C_DR=(DeviceAddress|0x01);//发送iic从器件物理地址,最低位1,读操作while((I2C_SR1&0x02)==0);//地址已经被发送temp=I2C_SR1;//清除ADDR标志位temp=I2C_SR3;while((I2C_SR1&0x40)==0);//等待接收数据寄存器非空read_data=I2C_DR;I2C_CR2&=0xfb;//读取数据下,发送stop必须禁止ack,才能释放从机temp=I2C_SR1;//清零BTF标志位temp=I2C_DR;IIC_Stop();returnread_data;}附上stm8s在IAR环境下的项目工程,包括了SPI、IIC、PWM、AWU、USART、EEPROM等片上硬件的初始化代码。http://download.csdn.NET/detail/devintt/9454188读操作:开始->发送外设地址 -> 开始->发送需要读取的寄存器地址-> 读一个字节-> (可能再读一个字节)->。。。-> 结束

    时间:2019-01-02 关键词: stm8s 主机通信 iic

  • STM32单片机的IIC硬件编程---查询等待方式

    IIC器件是一种介于高速和低速之间的嵌入式外围设备,其实总体来说,它的速度算是比较慢的。通常情况下,速度慢的器件意味着更多的等待,这对于精益求精的嵌入式工程师来说,简直就是一个恶梦,低速器件的存取数据实在是太浪费资源。如何面对这种低速设备,而使系统运行达到最优化?我觉得应当尽可能多的使用硬件完成,这样软件的开销便会减小,系统软件不用过多的时间去等待这些数据,而专注于硬件的请求和处理。 IIC协议,在笔者看来,其实并不是一种很好的协议,它没有较好的出错恢复机制,它是基于一种状态机模式的通讯协议,在这个状态转换中出现任意一步错误,将会导致总线不可恢复,极脆弱。在400KHZ的最高带通讯速率下,很多时候也极易产生干抗,因其采用了TTL电平传输数据,加上数字器件的状态识别问题,在高速时整个总线的状态极易产生崩溃,所以笔者的建议是,有其它接口的器件时,尽量不要用IIC接口器件……它远远没有想像中的那么可靠。 STM32系列CPU中提供了一些IIC的硬件模块,笔者针对它的一些特点,总结了一些使用方法,并按照一般程序员的使用习惯,提出了三种不同的编程和实现方式,分别是查询等待方式、硬件中断方式、WRTOS驱动集成方式。前两种不需要RTOS的支持。 下面先讨论STM32系列MCU的IIC硬件查询等待方式编程: 首先,根据该MCU的特点和寄存器定义,我们做一些有用的宏定义和引用: /*------------------------------------------------------------------------------------------------根据STM32系列MCU的寄存器定义产生的一些宏定义,这些是可以移植的,主要是为了统一硬件操作,否则程序看着不爽------------------------------------------------------------------------------------------------*/#defineI2C1_SET_ACKI2C1->CR1|=I2C_CR1_ACK;//设置ACK允许应答#defineI2C1_CLR_ACKI2C1->CR1&=~I2C_CR1_ACK;//清除ACK应答#defineI2C1_DATAI2C1->DR//I2C1数据寄地址#defineI2C1_STARTI2C1->CR1|=I2C_CR1_START;//启动I2C1#defineI2C1_STOPI2C1->CR1|=I2C_CR1_STOP;//停止I2C1#defineI2C1_CurMode(I2C1->SR2&I2C_SR2_MSL)//检查总线模式#defineI2C1_IsBusy(I2C1->SR2&I2C_SR2_BUSY)//检查总线忙标志#defineI2C1_TxReady(I2C1->SR1&I2C_SR1_TXE)//检查是否发送缓冲区为空#defineI2C1_RxReady(I2C1->SR1&I2C_SR1_RXNE)//检查是否接收到数据#defineI2C1_TxAddr(I2C1->SR1&I2C_SR1_ADDR)//检查地址是否已被发送#defineI2C1_TxStart(I2C1->SR1&I2C_SR1_SB)//检查起始位是否已被发送 任何一种硬件模块都有它自己的使用规则和使用方法,STM32系列的IIC也不例外,据笔者的体会,它的IIC操作过程有一些它自己的个性,如起始位的发送以及对状态寄存器的假读规则等,区别于其它MCU的IIC使用。 其实任何一个IIC模块,只会有两种应用,非读取写数据,下面是笔者锤练过的STM32系列MCU硬件IIC写数据方法,查询等待方式: /*--------------------------------------------------------------------Func:I2C1写入数据,查询等待方式---------------------------------------------------------------------*/voidI2C1_WriteBytes(uint8Addr,uint8*TxBuffer,uint8TxLenth){I2C1_SET_ACK//允许ACK应答I2C1_START//启动I2C总线while(!I2C1_TxStart);//等待起始位发送I2C1_DATA=Addr;//发送设备地址while(!I2C1_TxAddr);//等待地址发送结束Addr=I2C1_CurMode;//读SR2清标志(很重要,假读)while(TxLenth--){I2C1_DATA=*TxBuffer++;//发送缓冲区数据while(!I2C1_TxReady);//等待发送完成}I2C1_STOP//数据发送结束,释放总线} 对于IIC的写操作,先发送设备地址,得到响应后再发送数据,至少数据内容,以及长度,就不是本方法所关心的了,本方法可发送任意指定长度的数据包,前提是应当指定正确的TxLenth,当然,也可以通过判断最后一个字节的ACK请求得到结束位置,但笔者认为这样指定长度发送更好。至于IIC发送方法为什么是这样,请参考IIC的发送协议。 下面是IIC主机的读数据协议,它比写方式复杂了一点点: /*----------------------------------------------------------------------------Func:I2C1读取数据Note:DevAddr/从设备地址DataAddr/片内地址*RxBuffer/接收缓冲区RxLenth/接收长度-----------------------------------------------------------------------------*/voidI2C1_ReadBytes(uint8DevAddr,uint8DataAddr,uint8*RxBuffer,uint8RxLenth){I2C1_SET_ACK//允许ACK应答I2C1_START//启动I2C总线while(!I2C1_TxStart);//等待起始位发送I2C1_DATA=DevAddr;//发送地址while(!I2C1_TxAddr);//等待地址发送结束if(I2C1_CurMode);//读SR2清标志I2C1_DATA=DataAddr;//写数据地址while(!I2C1_TxReady);//等待写入完成I2C1_START//启动I2C总线----->注意,此处非常重要while(!I2C1_TxStart);//等待起始位发送I2C1_DATA=DevAddr|0x01;//发送地址while(!I2C1_TxAddr);//等待地址发送结束if(I2C1_CurMode);//读SR2清标志while(RxLenth--){ while(!I2C1_RxReady); //等待数据到来

    时间:2018-12-06 关键词: 单片机 STM32 硬件编程- 查询等待方式 iic

  • STM32 软件IIC接口,支持虚拟多个IIC接口

    STM32 软件IIC接口,支持虚拟多个IIC接口,需要自己对底层进行移植,比如IO口时钟使能,初始化,写1,写0,读取进行移植,移植到自己的硬件平台。//SoftwareIIC.c/*************************************************************************************************************  * 文件名: SoftwareIIC.c  * 功能: STM32 软件IIC接口  * 作者: cp1300@139.com  * 创建时间: 2017-07-10  * 最后修改时间: 2017-07-10  * 详细: 软件IIC接口 *************************************************************************************************************/ #include "system.h" #include "GPIO_INIT.h" #include "delay.h" #include "SoftwareIIC.h" //调试宏开关 #define SIIC_DBUG 1 #if SIIC_DBUG #include "system.h" #define SIIC_Debug(format,...) uart_printf(format,##__VA_ARGS__) #else #define SIIC_Debug(format,...) / / #endif //SIIC_DBUG //相关底层操作接口 #define SIIC_DelayUS(x) Delay_US(x) //延时US #define SDA_OUT_MODE(GPIOx,bit) GPIOx_OneInit(GPIOx,bit,OUT_PP,SPEED_2M) //SDA输出模式 #define SDA_IN_MODE(GPIOx,bit) GPIOx_OneInit(GPIOx,bit,IN_IPU,IN_IN) //SDA输入模式 #define SCL_OUT_MODE(GPIOx,bit) GPIOx_OneInit(GPIOx,bit,OUT_PP,SPEED_2M) //SCL输出模式 #define SDA_OUT_H(GPIOx,bit) (GPIOx->ODR|=(1<ODR&=~(1<ODR|=(1<ODR&=~(1<IDR&(1<<bit))?1:0) //SDA输入 /************************************************************************************************************************* *函数         : bool SIIC_Init(SIIC_HANDLE *pHandle, GPIO_TypeDef *SDA_GPIOx, GPIO_TypeDef *SCL_GPIOx,u8 SDA_GPIO_BITx, u8 SCL_GPIO_BITx,u8 DelayUS) *功能         : 软件IIC初始化 *参数         : pHandle:句柄;SDA_GPIOx:SDA IO;SCL_GPIOx:SCL GPIO;SDA_GPIO_BITx:SDA IO位数;SCL_GPIO_BITx:SCL IO位数;DelayUS:通信延时,单位us(1-100us) *返回         : TRUE:初始化成功;FALSE:初始化失败 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/ bool SIIC_Init(SIIC_HANDLE *pHandle, GPIO_TypeDef *SDA_GPIOx, GPIO_TypeDef *SCL_GPIOx,u8 SDA_GPIO_BITx, u8 SCL_GPIO_BITx,u8 DelayUS) { u8 DEV_GPION; if(pHandle == NULL)  { SIIC_Debug("初始化软件IIC失败,pHandle句柄为空rn"); return FALSE; } if(DelayUS < 1) DelayUS = 1; if(DelayUS >100) DelayUS = 100; pHandle->DelayUS = DelayUS; pHandle->SDA_GPIOx = SDA_GPIOx; //SDA数据线IO pHandle->SCL_GPIOx = SCL_GPIOx; //SCL时钟线IO if(SDA_GPIO_BITx > 15)  { SIIC_Debug("初始化软件IIC失败,SDA_GPIO_BITx不能超过15rn"); return FALSE; } pHandle->SDA_GPIO_BITx = SDA_GPIO_BITx; //SDA数据线位,0-15 if(SCL_GPIO_BITx > 15)  { SIIC_Debug("初始化软件IIC失败,SCL_GPIO_BITx不能超过15rn"); return FALSE; } pHandle->SCL_GPIO_BITx = SCL_GPIO_BITx; //SCL时钟线位,0-15 //判断SDA IO组,进行时钟初始化 switch((u32)SDA_GPIOx) { case (u32)GPIOA: { DEV_GPION = DEV_GPIOA; }break; case (u32)GPIOB: { DEV_GPION = DEV_GPIOB; }break; case (u32)GPIOC: { DEV_GPION = DEV_GPIOC; }break; case (u32)GPIOD: { DEV_GPION = DEV_GPIOD; }break; case (u32)GPIOE: { DEV_GPION = DEV_GPIOE; }break; case (u32)GPIOF: { DEV_GPION = DEV_GPIOF; }break; case (u32)GPIOG: { DEV_GPION = DEV_GPIOG; }break; default:  { SIIC_Debug("初始化软件IIC失败,SDA_GPIOx无效rn"); return FALSE; } } DeviceClockEnable(DEV_GPION, ENABLE); //使能SDA IO时钟 //判断SCL IO组,进行时钟初始化 switch((u32)SCL_GPIOx) { case (u32)GPIOA: { DEV_GPION = DEV_GPIOA; }break; case (u32)GPIOB: { DEV_GPION = DEV_GPIOB; }break; case (u32)GPIOC: { DEV_GPION = DEV_GPIOC; }break; case (u32)GPIOD: { DEV_GPION = DEV_GPIOD; }break; case (u32)GPIOE: { DEV_GPION = DEV_GPIOE; }break; case (u32)GPIOF: { DEV_GPION = DEV_GPIOF; }break; case (u32)GPIOG: { DEV_GPION = DEV_GPIOG; }break; default: { SIIC_Debug("初始化软件IIC失败,SCL_GPIOx无效rn"); return FALSE; } } DeviceClockEnable(DEV_GPION, ENABLE); //使能SCL IO时钟 SDA_OUT_MODE(SDA_GPIOx,SDA_GPIO_BITx); //SDA输出模式 SCL_OUT_MODE(SCL_GPIOx,SCL_GPIO_BITx); //SCL输出模式 SDA_OUT_H(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 return TRUE; } /************************************************************************************************************************* *函数         : void SIIC_Start(SIIC_HANDLE *pHandle) *功能         : 产生IIC起始信号 *参数         : pHandle:句柄 *返回         : 无 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/ void SIIC_Start(SIIC_HANDLE *pHandle) { SDA_OUT_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输出 SDA_OUT_H(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SIIC_DelayUS(pHandle->DelayUS); //延时   SDA_OUT_L(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=0 START:when CLK is high,DATA change form high to low  SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0,钳住I2C总线,准备发送或接收数据  SIIC_DelayUS(pHandle->DelayUS); //延时 } /************************************************************************************************************************* *函数         : void SIIC_Stop(SIIC_HANDLE *pHandle) *功能         : 产生IIC停止信号 *参数         : pHandle:句柄 *返回         : 无 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/ void SIIC_Stop(SIIC_HANDLE *pHandle) { SDA_OUT_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输出 SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0 SDA_OUT_L(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=0 //STOP:when CLK is high DATA change form low to high SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SDA_OUT_H(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 SIIC_DelayUS(pHandle->DelayUS); //延时     } /************************************************************************************************************************* *函数         : bool SIIC_WaitAck(SIIC_HANDLE *pHandle) *功能         : IIC等待应答信号到来 *参数         : pHandle:句柄 *返回         : TRUE:应答成功;FALSE:应答失败 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/ bool SIIC_WaitAck(SIIC_HANDLE *pHandle) { u8 ucErrTime=0; SDA_IN_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输入 SDA_OUT_H(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SIIC_DelayUS(pHandle->DelayUS); //延时   while(SDA_IN(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx)) //等待低电平 { ucErrTime++; if(ucErrTime>50) { SIIC_Stop(pHandle); return FALSE; } SIIC_DelayUS(1); } SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0     return TRUE;   }  /************************************************************************************************************************* *函数         : void SIIC_Ack(SIIC_HANDLE *pHandle) *功能         : IIC产生ACK应答 *参数         : pHandle:句柄 *返回         : 无 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/ void SIIC_Ack(SIIC_HANDLE *pHandle) { SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0     SDA_OUT_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输出 SDA_OUT_L(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=0 SIIC_DelayUS(pHandle->DelayUS); //延时   SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0   SIIC_DelayUS(pHandle->DelayUS); //延时 } /************************************************************************************************************************* *函数         : void SIIC_NAck(SIIC_HANDLE *pHandle) *功能         : SIIC产生NACK应答 *参数         : pHandle:句柄 *返回         : 无 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/      void SIIC_NAck(SIIC_HANDLE *pHandle) { SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0     SDA_OUT_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输出 SDA_OUT_H(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0    SIIC_DelayUS(pHandle->DelayUS); //延时 } /************************************************************************************************************************* *函数         : bool SIIC_SendByte(SIIC_HANDLE *pHandle, u8 data) *功能         : SIIC发送一个字节 *参数         : pHandle:句柄;data:要发送的数据 *返回         : TRUE:有应答;FALSE:无应答 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/      bool SIIC_SendByte(SIIC_HANDLE *pHandle, u8 data) {                             u8 t;  SDA_OUT_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输出          SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0 拉低时钟开始数据传输     for(t=0;tSDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=1 } else { SDA_OUT_L(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA=0 }         data <DelayUS); //延时 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1 SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_L(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0 SIIC_DelayUS(pHandle->DelayUS); //延时     } return SIIC_WaitAck(pHandle); //等待应答 }  /************************************************************************************************************************* *函数         : u8 SIIC_ReadByte(SIIC_HANDLE *pHandle,bool isAck) *功能         : SIIC读取一个字节 *参数         : pHandle:句柄;isAck:是否发送ACK *返回         : 读取到的数据 *依赖 :  底层宏定义 *作者        : cp1300@139.com *时间      : 2017-07-10 *最后修改时间 : 2017-07-10 *说明         : 无 *************************************************************************************************************************/  u8 SIIC_ReadByte(SIIC_HANDLE *pHandle,bool isAck) { u8 i,receive=0; SDA_IN_MODE(pHandle->SDA_GPIOx, pHandle->SDA_GPIO_BITx); //SDA设置为输入     for(i=0;iSCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=0         SIIC_DelayUS(pHandle->DelayUS); //延时 SCL_OUT_H(pHandle->SCL_GPIOx, pHandle->SCL_GPIO_BITx); //SCL=1         receive<SDA_GPIOx, pHandle->SDA_GPIO_BITx))receive++;    SIIC_DelayUS(pHandle->DelayUS); //延时     }       if (!isAck)         SIIC_NAck(pHandle);//发送nACK     else         SIIC_Ack(pHandle); //发送ACK       return receive; }//SoftwareIIC.H/*************************************************************************************************************  * 文件名: SoftwareIIC.H  * 功能: STM32 软件IIC接口  * 作者: cp1300@139.com  * 创建时间: 2017-07-10  * 最后修改时间: 2017-07-10  * 详细: 软件IIC接口 *************************************************************************************************************/ #ifndef _SOFTWARE_IIC_H_ #define _SOFTWARE_IIC_H_ #include "system.h" //软件IIC接口句柄结构 typedef struct  { GPIO_TypeDef *SDA_GPIOx; //SDA数据线IO GPIO_TypeDef *SCL_GPIOx; //SCL时钟线IO u8 SDA_GPIO_BITx; //SDA数据线位,0-15 u8 SCL_GPIO_BITx; //SCL时钟线位,0-15 u8  DelayUS; //通信延时US }SIIC_HANDLE; bool SIIC_Init(SIIC_HANDLE *pHandle, GPIO_TypeDef *SDA_GPIOx, GPIO_TypeDef *SCL_GPIOx,u8 SDA_GPIO_BITx, u8 SCL_GPIO_BITx,u8 DelayUS); //软件IIC初始化 void SIIC_Start(SIIC_HANDLE *pHandle); //产生IIC起始信号 void SIIC_Stop(SIIC_HANDLE *pHandle); //产生IIC停止信号 bool SIIC_WaitAck(SIIC_HANDLE *pHandle); //IIC等待应答信号到来 void SIIC_Ack(SIIC_HANDLE *pHandle); //IIC产生ACK应答 void SIIC_NAck(SIIC_HANDLE *pHandle); //SIIC产生NACK应答 bool SIIC_SendByte(SIIC_HANDLE *pHandle, u8 data); //SIIC发送一个字节 u8 SIIC_ReadByte(SIIC_HANDLE *pHandle,bool isAck); //SIIC读取一个字节 #endif /*_SOFTWARE_IIC_H_*///初始化测试static SIIC_HANDLE IIC_Handel; #define PCF8563_SDA_GPIOx  GPIOB #define PCF8563_SCL_GPIOx  GPIOB #define PCF8563_SDA_Bit  9 #define PCF8563_SCL_Bit   8 if(SIIC_Init(&IIC_Handel,PCF8563_SDA_GPIOx, PCF8563_SCL_GPIOx, PCF8563_SDA_Bit, PCF8563_SCL_Bit, 1) == FALSE) { PCF8563_Debug("**********************PCF8563 初始化失败,IIC接口初始化失败!rn"); return FALSE; }//读写测试SIIC_Start(&IIC_Handel); //发送起始信号 if(SIIC_SendByte(&IIC_Handel, PCF8563_IIC_W_ADDR)==FALSE) //发送写地址 { PCF8563_Debug("PCF8563 发送写地址 ACK错误rn"); return FALSE; } if(SIIC_SendByte(&IIC_Handel, RegIndex)==FALSE) //发送要读取的寄存器地址 { PCF8563_Debug("PCF8563 发送要读取的寄存器地址2 ACK错误rn"); return FALSE; } SIIC_Start(&IIC_Handel); //发送起始信号

    时间:2018-11-21 关键词: STM32 io口 iic

  • STM32-IIC 配置解说

    STM32-IIC 配置解说(原创)STM32 - I2C 简介 :I2C 总线接口连接微控制器和串行 I2C 总线。它提供多主机功能,控制所有 I2C总线特定的时序、协议、仲裁和定时。支持标准和快速两种模式,另外 STM32的 I2C 可以使用 DMA 方式操作。本文主要以一个实例来介绍 STM32-I2C 的配置方式和具体在工程中通过调用哪些库函数来实现I2C 器件的通信。实例:写入数据到器件 AT24C02 并将存入的数据读出好,我们先来讲讲 STM32 I2C 模块的端口基本配置,由 STM32 中文参考手册可以查到在使用 I2C 时对应的引脚要配置成哪种模式。 SCL 和 SDA 引脚都配置成开漏复用输出本人用的是 STM32F103VET6,它有 2 个 I2C 接口。 I/O 口定义为 PB6-I2C_SCL,PCB7-I2C1_SDA; PB10-I2C_SCL, PB11-I2C_SDA,由手册可以查出对应的端口。图文如下:调用库函数将 I2C 端口配置好(本文使用的是 PB6、 PB7 端口):程序代码如下:void I2C_GPIO_Config(void){GPIO_InitTypeDef GPIO_InitStructure; //GPIO 结构体定义RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能 I2C 的 IO 口GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化结构体配置}void I2C_Mode_config(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 =0x0A;I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_InitStructure.I2C_ClockSpeed = 400000;I2C_Cmd(I2C1, ENABLE);I2C_Init(I2C1, &I2C_InitStructure);}好了, STM32 内部的 I2C 模块工作模式就这样被设好了,接下来需要完成与外部器件AT24C02( EEPROM)进行通信。将分两部分进行代码解析,第一部分是:对 AT24C02 进行写操作,第二部分:对 AT24C02 进行读操作。第一部分(写):备注: I2C_PageSize 为宏定义 #define I2C_PageSize 8 ;void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite){u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;Addr = WriteAddr % I2C_PageSize;//查看输入的地址是不是 8 的整数倍count = I2C_PageSize - Addr;//表示距离下一页页首地址的距离(步伐数)NumOfPage = NumByteToWrite / I2C_PageSize;//算出一共有多少页NumOfSingle = NumByteToWrite % I2C_PageSize;//算出不够一页的数据的余数if(Addr == 0) //如果输入的地址是首页地址{if(NumOfPage == 0) //如果不足一页数据{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不够一页的余数作为实参I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作}else //如果数据有一页以上{while(NumOfPage--)//用一个 while 循环,执行页写循环操作,有多少页就写多少次{I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); //调用写函数,将I2C_PageSize 变量作为实参执行页写I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作WriteAddr += I2C_PageSize;//每执行完一次页写对应的地址也需要移 8 个位pBuffer += I2C_PageSize;//数据指针移 8 个位}if(NumOfSingle!=0)//如果有不足一页的数据余数则执行{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle不够一页的余数作为实参I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作}}}else //输入的地址不是首页地址{if(NumOfPage== 0) //如果不足一页{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不够一页的余数作为实参I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作}else//如果有一页或一页以上{NumByteToWrite -= count;//将地址后续的缺省位置补上数据,数据的多少就是 count的值, NumByteToWrite 变量的值就是补上数据之后还剩下未发送的数量NumOfPage = NumByteToWrite / I2C_PageSize;//剩余的页数NumOfSingle = NumByteToWrite % I2C_PageSize;//不足一页的数据数量if(count != 0)//将地址后续的缺省位置补上数据{I2C_EE_PageWrite(pBuffer, WriteAddr, count);//调用写函数,以 count 为实参,将地址缺省下来的部分地址给填充上数据I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作WriteAddr += count;//加上 count 后,地址就移位到下一页的首地址pBuffer += count;//数据指针移 count 个位}while(NumOfPage--)//将剩余的页数数据写入 EEPROM{I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);//调用写函数,将I2C_PageSize 变量作为实参执行页写I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作WriteAddr += I2C_PageSize;//将地址移 8 个位pBuffer += I2C_PageSize; //将数据指针移 8 个位}if(NumOfSingle != 0)//将不足一页的数据写入 EEPROM{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle不够一页的余数作为实参I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作}}}}在以上写操作里面我们拿经常被调用的 I2C_EE_PageWrite 函数还有I2C_EE_WaitEepromStandbyState 函数并结合 STM32 中文参考手册图文进行对照分析请读者在读 I2C_EE_PageWrite 函数时请结合上述时序图和下述代码联系一起看!注: EEPROM_ADDRESS 为器件的地址,大家按照自己具体器件地址写入即可,例: #define EEPROM_ADDRESS 0xA0void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite){I2C_GenerateSTART(I2C1, ENABLE);//产生起始位while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//ADDR=1,清除 EV6I2C_SendData(I2C1, WriteAddr); //EEPROM 的具体存储地址位置while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄存器非空,数据寄存器已经空,产生 EV8,发送数据到 DR 既可清除该事件while(NumByteToWrite--) //利用 while 循环 发送数据{I2C_SendData(I2C1, *pBuffer); //发送数据pBuffer++; //数据指针移位while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除EV8}I2C_GenerateSTOP(I2C1, ENABLE);//产生停止信号}I2C_EE_WaitEepromStandbyState 这个函数,在每调用完写操作函数后都调用这个函数,这个函数是用来检测 EEPROM 器件是否已经完成内部写的操作,判断器件完成操作后在进行下一步的操作!代码如下:void I2C_EE_WaitEepromStandbyState(void){vu16 SR1_Tmp = 0;do{I2C_GenerateSTART(I2C1, ENABLE);//产生起始信号SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);//读 SR1 寄存器I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件地址清除事件}while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));//如果接收不到从机的应答( NACK)则说明 EEPROM 器件还在工作,直到完成操作跳出循环体!I2C_ClearFlag(I2C1, I2C_FLAG_AF);//清除 AF 标志位I2C_GenerateSTOP(I2C1, ENABLE); //产生停止信号}第二部分(读):由以上 AT24C02 读时序图可以知道:读部分需要产生两次起始信号另外:主设备在从从设备接收到最后一个字节后发送一个 NACK 。接收到 NACK 后,从设备释放对 SCL 和 SDA 线的控制;主设备就可以发送一个停止/ 重起始条件。● 为了在收到最后一个字节后产生一个 NACK 脉冲,在读倒数第二个数据字节之后(在倒数第二个 RxNE 事件之后)必须清除 ACK 位。● 为了产生一个停止/ 重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二个 RxNE 事件之后)设置 STOP/START 位。● 只接收一个字节时,刚好在 EV6 之后(EV6_1 时,清除 ADDR 之后)要关闭应答和停止条件的产生位。请读者将代码和图结合在一起看!void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//需要两个起始信号{while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //调用库函数检测 I2C 器件是否处于 BUSY 状态I2C_GenerateSTART(I2C1, ENABLE);//开启信号while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//写入器件地址while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6I2C_SendData(I2C1, ReadAddr); //发送读的地址while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除 EV8I2C_GenerateSTART(I2C1, ENABLE);//开启信号while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);//将器件地址传出,主机为读while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//清除EV6while(NumByteToRead){if(NumByteToRead == 1)//只剩下最后一个数据时进入 if 语句{I2C_AcknowledgeConfig(I2C1, DISABLE);//最后有一个数据时关闭应答位I2C_GenerateSTOP(I2C1, ENABLE);//最后一个数据时使能停止位}if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) //读取数据{*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBufferpBuffer++; //指针移位NumByteToRead--;//字节数减 1}}I2C_AcknowledgeConfig(I2C1, ENABLE);//将应答位使能回去,等待下次通信}STM32-IIC 配置解说到此告一段落!如果有不正确的地方也请各位多多指教,本人及时纠正;欢迎大家来和我相互交流学习,谢谢大家。

    时间:2018-11-08 关键词: STM32 iic

  • 24C01的IIC 读写的C51程序

    /*------------------------------------------------------------------------------为了安全起见,程序中很多NOP是冗余的,希望读者能进一步精简,但必须经过验证。 Atmel 24C01 比较特殊,为简约型. 51晶振为11.0592MHz〖参考〗http://www.c51bbs.com〖版本〗V1.00A Build 0918-----------------------------------------------------------------------------*/#include "reg51.h"#include "intrins.h"sbit SCL= P1^5;sbit SDA= P1^4;delay(){ unsigned int i=1200; while(i--);}/*----------------------------------------------------------------------------调用方式:write_8bit(unsigned char ch)﹫2001/03/23函数说明:内函数,私有,用户不直接调用。-------------------------------------------------------------------------------*/voidwrite_8bit(unsigned char ch){ unsigned char i=8; SCL=0; _nop_();_nop_();_nop_();_nop_();_nop_(); while (i--) { SDA=(bit)(ch&0x80); _nop_();_nop_();_nop_();_nop_();_nop_(); ch

    时间:2018-11-05 关键词: 读写 c51程序 24c01 iic

  • 集成电路总线(iic)介绍

    对于iic这个(Inter-Integrated Circuit)集成电路之间的通信的这个称呼还很陌生的,只知道是用来通信的一个桥梁,他可能还有一个名字——集成电路总线。    对于集成电路总线(iic)还要从代码中说起,我最开始是从STM32开发板学起的没学过51单片机(其实是学过了但一点印象都没有毕竟我们学校有开过单片机这门课怎么可能没讲51呢)。我要做的项目是要采集MPU6050模块的数据,我在做这个项目中看过正点原子的例程他们是有这个例程讲解的,我以为修修改改输入方式把例程中的程序忘开发板一装就OK了,结果给我显示mpu6050初始化不成功的提示(运行的程序我有加判断语句的习惯)。我当时的反应:是我勒个去,什么情况。经过查找结果发现是我用的iic的两根总线——数据线和时钟线 的地址出现错误,我用的是PB6、PB7口而例程中提供的例程是PC口的地址,一塌糊涂。就去从新看了如何修改IIC的视频及GPIO的视频学习如何改iic的配置寄存器。起码我现在会对例程修改配置寄存器,对于iic的底层的通信协议了解一些、对应用的通信协议了解大概。但对应用的通信协议还有不懂得,看来对iic还需继续学习和应用。再补点知识:I2CInter-Integrated Circuit总线是用于连接微控制器及其外围设备。发送器:发送数据到总线的器件;接收器:从总线接收数据的器件;主机:启动数据传送并产生时钟信号的设备;从机:被主机寻址的器件;1、I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备(特别是外部存储器件)。 2、UART通信只需要一条线即可完成,I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。 3、从应用上来讲 ,UART通信多用于板间通信,I2C通信多用于板内通信。

    时间:2018-10-23 关键词: 集成电路总线 iic

  • SPI,IIC和SMbus的应用优缺点分析

    总结: SPI有较快的速度,但是只能单主多从,管理线比较复杂。 IIC等速度比较慢,数据比较臃余,但是主从管理好,也省电省控制管脚。 概述: 对于需要经常进行数据流传输的系统数据,SPI是首选,因为它拥有较快的时钟速率,速率可从几兆赫兹到几十兆赫兹。然而,对于系统管理活动,如读取温度传感器的读数和查询多个从器件的状态,或者需要多个主器件共存于同一系统总线上(系统冗余常会要求这一点),或者面向低功耗应用,这时I2C 或 SMBus将是首选接口。 下面几部分将介绍每种串行总线及其优缺点。 1. SPI SPI 是一种四线制串行总线接口,为主/从结构,四条导线分别为串行时钟(SCLK)、主出从入(MOSI)、主入从出(MISO)和从选(SS)信号。主器件为时钟提供者,可发起读从器件或写从器件操作。这时主器件将与一个从器件进行对话。当总线上存在多个从器件时,要发起一次传输,主器件将把该从器件选择线拉低,然后分别通过 MOSI 和 MISO 线启动数据发送或接收。 SPI 时钟速度很快,范围可从几兆赫兹到几十兆赫兹,且没有系统开销。SPI 在系统管理方面的缺点是缺乏流控机制,无论主器件还是从器件均不对消息进行确认,主器件无法知道从器件是否繁忙。因此,必须设计聪明的软件机制来处理确认问题。同时,SPI 也没有多主器件协议,必须采用很复杂的软件和外部逻辑来实现多主器件架构。每个从器件需要一个单独的从选择信号。总信号数最终为 n+3 个,其中 n 是总线上从器件的数量。因此,导线的数量将随增加的从器件的数量按比例增长。同样,在 SPI 总线上添加新的从器件也不方便。对于额外添加的每个从器件,都需要一条新的从器件选择线或解码逻辑。图 2 显示了典型的 SPI 读/写周期。在地址或命令字节后面跟有一个读/写位。数据通过 MOSI 信号写入从器件,通过 MISO 信号自从器件中读出。图 3显示了 I2C总线/SMBus以及SPI的系统框图。 2. I2C总线 I2C 是一种二线制串行总线接口,工作在主/从模式。二线通信信号分别为开漏 SCL 和 SDA 串行时钟和串行数据。主器件为时钟源。数据传输是双向的,其方向取决于读/写位的状态。每个从器件拥有一个唯一的 7 或 10 位地址。主器件通过一个起始位发起一次传输,通过一个停止位终止一次传输。起始位之后为唯一的从器件地址,再后为读/写位。 I2C总线速度为从0Hz到3.4MHz。它没有SPI 那样快,但对于系统管理器件如温度传感器来说则非常理想。I2C 存在系统开销,这些开销包括起始位/停止位、确认位和从地址位,但它因此拥有流控机制。主器件在完成接收来自从器件的数据时总是发送一个确认位,除非其准备终止传输。从器件在其接收到来自主器件的命令或数据时总是发送一个确认位。当从器件未准备好时,它可以保持或延展时钟,直到其再次准备好响应。 I2C允许多个主器件工作在同一总线上。多个主器件可以轻松同步其时钟,因此所有主器件均采用同一时钟进行传输。多个主器件可以通过数据仲裁检测哪一个主器件正在使用总线,从而避免数据破坏。由于 I2C总线只有两条导线,因此新从器件只需接入总线即可,而无需附加逻辑。 3. SMBus SMBus是一种二线制串行总线,1996年第一版规范开始商用。它大部分基于I2C总线规范。和 I2C一样,SMBus不需增加额外引脚,创建该总线主要是为了增加新的功能特性,但只工作在100kHz且专门面向智能电池管理应用。它工作在主/从模式:主器件提供时钟,在其发起一次传输时提供一个起始位,在其终止一次传输时提供一个停止位;从器件拥有一个唯一的7或10位从器件地址。 SMBus与I2C总线之间在时序特性上存在一些差别。首先,SMBus需要一定数据保持时间,而 I2C总线则是从内部延长数据保持时间。SMBus具有超时功能,因此当SCL太低而超过35 ms时,从器件将复位正在进行的通信。相反,I2C采用硬件复位。SMBus具有一种警报响应地址(ARA),因此当从器件产生一个中断时,它不会马上清除中断,而是一直保持到其收到一个由主器件发送的含有其地址的ARA为止。SMBus只工作在从10kHz到最高100kHz。最低工作频率10kHz是由SMBus超时功能决定的。

    时间:2018-10-19 关键词: 接口 spi 电源技术解析 smbus miso iic

  • IIC和SPI总线协议简介

    IIC和SPI总线协议简介

    IICvs SPI         现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影。原因是这两种通信协议非常适合近距离低速芯片间通信。Philips(for IIC)和Motorola(for SPI) 出于不同背景和市场需求制定了这两种标准通信协议。         IIC 开发于1982年,当时是为了给电视机内的CPU和外围芯片提供更简易的互联方式。电视机是最早的嵌入式系统之一,而最初的嵌入系统是使用内存映射(memory-mapped I/O)的方式来互联微控制器和外围设备的。要实现内存映射,设备必须并联入微控制器的数据线和地址线,这种方式在连接多个外设时需大量线路和额外地址解码芯片,很不方便并且成本高。         为了节省微控制器的引脚和和额外的逻辑芯片,使印刷电路板更简单,成本更低,位于荷兰的Philips实验室开发了 ‘Inter-Integrated Circuit’,IIC 或 IIC ,一种只使用二根线接连所有外围芯片的总线协议。最初的标准定义总线速度为100kbps。经历几次修订,主要是1995年的400kbps,1998的3.4Mbps。         有迹象表明,SPI总线首次推出是在1979年,Motorola公司将SPI总线集成在他们第一支改自68000微处理器的微控制器芯片上。SPI总线是微控制器四线的外部总线(相对于内部总线)。与IIC不同,SPI没有明文标准,只是一种事实标准,对通信操作的实现只作一般的抽象描述,芯片厂商与驱动开发者通过data sheets和application notes沟通实现上的细节。SPI     对于有经验的数字电子工程师来说,用SPI互联两支数字设备是相当直观的。SPI是种四根信号线协议(如图):§ SCLK: Serial Clock (output from master);§ MOSI; SIMO: Master Output, Slave Input(output from master);§ MISO; SOMI: Master Input, Slave Output(output from slave);§ SS: Slave Select (active low, outputfrom master).        SPI是[单主设备( single-master )]通信协议,这意味着总线中的只有一支中心设备能发起通信。当SPI主设备想读/写[从设备]时,它首先拉低[从设备]对应的SS线(SS是低电平有效),接着开始发送工作脉冲到时钟线上,在相应的脉冲时间上,[主设备]把信号发到MOSI实现“写”,同时可对MISO采样而实现“读”,如下图:         SPI有四种操作模式——模式0、模式1、模式2和模式3,它们的区别是定义了在时钟脉冲的哪条边沿转换(toggles)输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低)。每种模式由一对参数刻画,它们称为时钟极(clock polarity)CPOL与时钟期(clock phase)CPHA。[主从设备]必须使用相同的工作参数——SCLK、CPOL 和 CPHA,才能正常工作。如果有多个[从设备],并且它们使用了不同的工作参数,那么[主设备]必须在读写不同[从设备]间重新配置这些参数。以上SPI总线协议的主要内容。SPI不规定最大传输速率,没有地址方案;SPI也没规定通信应答机制,没有规定流控制规则。事实上,SPI[主设备]甚至并不知道指定的[从设备]是否存在。这些通信控制都得通过SPI协议以外自行实现。例如,要用SPI连接一支[命令-响应控制型]解码芯片,则必须在SPI的基础上实现更高级的通信协议。SPI并不关心物理接口的电气特性,例如信号的标准电压。在最初,大多数SPI应用都是使用间断性时钟脉冲和以字节为单位传输数据的,但现在有很多变种实现了连续性时间脉冲和任意长度的数据帧。IIC         与SPI的单主设备不同,IIC 是多主设备的总线,IIC没有物理的芯片选择信号线,没有仲裁逻辑电路,只使用两条信号线—— ‘serial data’ (SDA) 和 ‘serial clock’ (SCL)。IIC协议规定:§ 第一,每一支IIC设备都有一个唯一的七位设备地址;§ 第二,数据帧大小为8位的字节;§ 第三,数据(帧)中的某些数据位用于控制通信的开始、停止、方向(读写)和应答机制。IIC 数据传输速率有标准模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps),另外一些变种实现了低速模式(10 kbps)和快速+模式(1 Mbps)。物理实现上,IIC 总线由两根信号线和一根地线组成。两根信号线都是双向传输的,参考下图。IIC协议标准规定发起通信的设备称为主设备,主设备发起一次通信后,其它设备均为从设备。IIC 通信过程大概如下。首先,主设备发一个START信号,这个信号就像对所有其它设备喊:请大家注意!然后其它设备开始监听总线以准备接收数据。接着,主设备发送一个7位设备地址加一位的读写操作的数据帧。当所设备接收数据后,比对地址自己是否目标设备。如果比对不符,设备进入等待状态,等待STOP信号的来临;如果比对相符,设备会发送一个应答信号——ACKNOWLEDGE作回应。当主设备收到应答后便开始传送或接收数据。数据帧大小为8位,尾随一位的应答信号。主设备发送数据,从设备应答;相反主设备接数据,主设备应答。当数据传送完毕,主设备发送一个STOP信号,向其它设备宣告释放总线,其它设备回到初始状态。基于IIC总线的物理结构,总线上的START和STOP信号必定是唯一的。另外,IIC总线标准规定SDA线的数据转换必须在SCL线的低电平期,在SCL线的高电平期,SDA线的上数据是稳定的。在物理实现上,SCL线和SDA线都是漏极开路(open-drain),通过上拉电阻外加一个电压源。当把线路接地时,线路为逻辑0,当释放线路,线路空闲时,线路为逻辑1。基于这些特性,IIC设备对总线的操作仅有“把线路接地”——输出逻辑0。IIC总线设计只使用了两条线,但相当优雅地实现任意数目设备间无缝通信,堪称完美。我们设想一下,如果有两支设备同时向SCL线和SDA线发送信息会出现什么情况。基于IIC总线的设计,线路上不可能出现电平冲突现象。如果一支设备发送逻辑0,其它发送逻辑1,那么线路看到的只有逻辑0。也就是说,如果出现电平冲突,发送逻辑0的始终是“赢家”。总线的物理结构亦允许主设备在往总线写数据的同时读取数据。这样,任何设备都可以检测冲突的发生。当两支主设备竞争总线的时候,“赢家”并不知道竞争的发生,只有“输家”发现了冲突——当它写一个逻辑1,却读到0时——而退出竞争。10位设备地址         任何IIC设备都有一个7位地址,理论上,现实中只能有127种不同的IIC设备。实际上,已有IIC的设备种类远远多于这个限制,在一条总线上出现相同的地址的IIC设备的概率相当高。为了突破这个限制,很多设备使用了双重地址——7位地址加引脚地址(external configuration pins)。IIC 标准也预知了这种限制,提出10位的地址方案。10位的地址方案对 IIC协议的影响有两点:§ 第一,地址帧为两个字节长,原来的是一个字节;§ 第二,第一个字节前五位最高有效位用作10位地址标识,约定是“11110”。除了10位地址标识,标准还预留了一些地址码用作其它用途,如下表:时钟拉伸        在 IIC 通信中,主设备决定了时钟速度。因为时钟脉冲信号是由主设备显式发出的。但是,当从设备没办法跟上主设备的速度时,从设备需要一种机制来请求主设备慢一点。这种机制称为时钟拉伸,而基于I²C结构的特殊性,这种机制得到实现。当从设备需要降低传输的速度的时候,它可以按下时钟线,逼迫主设备进入等待状态,直到从设备释放时钟线,通信才继续。高速模式       原理上讲,使用上拉电阻来设置逻辑1会限制总线的最大传输速度。而速度是限制总线应用的因素之一。这也说明为什么要引入高速模式(3.4 Mbps)。在发起一次高速模式传输前,主设备必须先在低速的模式下(例如快速模式)发出特定的“High Speed Master”信号。为缩短信号的周期和提高总线速度,高速模式必须使用额外的I/O缓冲区。另外,总线仲裁在高速模式下可屏蔽掉。更多的信息请参与总线标准文档。IIC vs SPI: 哪位是赢家?         我们来对比一下IIC 和 SPI的一些关键点:第一,总线拓扑结构/信号路由/硬件资源耗费       IIC 只需两根信号线,而标准SPI至少四根信号,如果有多个从设备,信号需要更多。一些SPI变种虽然只使用三根线——SCLK, SS和双向的MISO/MOSI,但SS线还是要和从设备一对一根。另外,如果SPI要实现多主设备结构,总线系统需额外的逻辑和线路。用IIC 构建系统总线唯一的问题是有限的7位地址空间,但这个问题新标准已经解决——使用10位地址。从第一点上看,IIC是明显的大赢家。第二,数据吞吐/传输速度          如果应用中必须使用高速数据传输,那么SPI是必然的选择。因为SPI是全双工,IIC 的不是。SPI没有定义速度限制,一般的实现通常能达到甚至超过10 Mbps。IIC 最高的速度也就快速+模式(1 Mbps)和高速模式(3.4 Mbps),后面的模式还需要额外的I/O缓冲区,还并不是总是容易实现的。第三,优雅性         IIC 常被称更优雅于SPI。公正的说,我们更倾向于认为两者同等优雅和健壮。IIC的优雅在于它的特色——用很轻盈的架构实现了多主设备仲裁和设备路由。但是对使用的工程师来讲,理解总线结构更费劲,而且总线的性能不高。SPI的优点在于它的结构相当的直观简单,容易实现,并且有很好扩展性。SPI的简单性不足称其优雅,因为要用SPI搭建一个有用的通信平台,还需要在SPI之上构建特定的通信协议软件。也就是说要想获得SPI特有而IIC没有的特性——高速性能,工程师们需要付出更多的劳动。另外,这种自定的工作是完全自由的,这也说明为什么SPI没有官方标准。IIC和SPI都对低速设备通信提供了很好的支持,不过,SPI适合数据流应用,而IIC更适合“字节设备”的多主设备应用。小结       在数字通信协议簇中,IIC和SPI常称为“小”协议,相对Ethernet, USB, SATA, PCI-Express等传输速度达数百上千兆字节每秒的总线。但是,我们不能忘记的是各种总线的用途是什么。“大”协议是用于系统外的整个系统之间通信的,“小”协议是用于系统内各芯片间的通信,没有迹象表明“大”协议有必要取代“小”协议。IIC和SPI的存在和流行体现了“够用就好”的哲学。回应文首,IIC和SPI如此的流行,它是任何一位嵌入式工程师必备的工具。

    时间:2018-10-11 关键词: 总线协议 iic

  • SPI IIC USART 区别

    第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART(Universal Asynchronous Receiver Transmitter:通用异步收发器)第二,区别在电气信号线上: SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信,当有多个从设备时,还可以增加一条从设备选择线。 如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO),一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。 I2C总线是双向、两线(SCL、SDA)、串行、多主控(multi-master)接口标准,具有总线仲裁机制,非常适合在器件之间进行近距离、非经常性的数据通信。在它的协议体系中,传输数据时都会带上目的设备的设备地址,因此可以实现设备组网。 如果用通用IO口模拟I2C总线,并实现双向传输,则需一个输入输出口(SDA),另外还需一个输出口(SCL)。(注:I2C资料了解得比较少,这里的描述可能很不完备) UART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。 显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。第三,从第二点明显可以看出,SPI和UART可以实现全双工,但I2C不行;第四,看看牛人们的意见吧! wudanyu:I2C线更少,我觉得比UART、SPI更为强大,但是技术上也更加麻烦些,因为I2C需要有双向IO的支持,而且使用上拉电阻,我觉得抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信。SPI实现要简单一些,UART需要固定的波特率,就是说两位数据的间隔要相等,而SPI则无所谓,因为它是有时钟的协议。 quickmouse:I2C的速度比SPI慢一点,协议比SPI复杂一点,但是连线也比标准的SPI要少。SPI总线----串行外围设备接口SPI(serial peripheral interface)总线技术是Motorola公司推出的一种同步串行接口。Motorola公司生产的绝大多数MCU(微控制器)都配有SPI硬件接口,如68系列MCU。SPI总线是一种三线同步总线,因其硬件功能很强,所以,与SPI有关的软件就相当简单,使CPU有更多的时间处理其他事务。IIC总线是荷兰飞利浦PHILIPS开发的一种高效,实用,可靠的双向二线制(也有3线制,家电很少用)串行数据传输结构总线,该总线使各电路分割成各种功能的模块,并进行软件化设计,各个功能模块电路内都有集成一个IIC总线接口电路,因此都可以挂接在总线上,很好的解决了众多功能IC与CPU之间的输入输出接口,使其连接方式变得十分简单。IIC总线上的器件分为主控器和被控器两大类它们之间只要在正常工作,总有一个IIC在总线上发送信息数据(一般是在开机后cpu首先像各个功能模块电路发出自检信号,得到各个功能模块电路正常反馈的数据信号后机器才进入正常工作状态)。 串行外围设备接口SPI(serial peripheral interface)总线技术是Motorola公司推出的一种同步串行接口 USART (Universial Serial Asynchronous Receive Transmit)通用串行异步接受/发送,其实它就是UART。 两者都是串行口线。但不同的是,SPI总线似乎是分为SD(DATA)I和SC(CONTROL)I两种。SPI是同步的,需要额外的同步脉冲,速率可以很高,有些器件甚至超过20M。SDI有一条数据线,双向传输,方向靠命令字来区别,SPI为三线:SO、SI、SCK,SO、SI均为单向传输,其中SCK为同步时钟,其传输速率可变,可以软件模拟实现;而SCI,有两条数据线,分别是输入输出。SPI 均有一条时钟线。但是SPI如果应用与高速场合,只能在板级使用,不宜传输过远,我们平时看到的SPI和IIC一般都是芯片之间的接口,很少看到有长电缆使用。 而USART是没有时钟线,数据线有两条。波特率在收发两端分别设置,51的串口就属于这一类。UART是异步的,不需要时钟,波特率相对不高;鉴于此,uart因为格式个波特率是双方协商好的,而且速率不高,所以可以在传输线上传输,加上其他的转换还可以变成差动的传输更远距离。 而UART要达到某一固定波特率对晶振有要求,比如说24M的晶振可以用T2实现9600BAUD,但不能用T1来交替实现19200和9600BAUD。

    时间:2018-10-08 关键词: spi usart iic

  • S3C2440 IIC读写AT24C02A

    S3C2440读写AT24C02A只需要采用主机发送模式和主机接收模式即可,手册上提供有这两个模式的流程图,可以参考。AT24C02A有几点需要注意的:1.AT24C02A连续读多个字节时最后一个字节不用产生应答信号。2.读的时候要先用写的方式写入硬件地址写方式和数据地址,此时不用发送STOP信号,接着继续写入硬件地址读方式,然后开始读数据。3.其写的方式只有单字节写或者页写两种,页写时每次只能写一页不能超过,AT24C02A的一页大小是8字节。以下是程序:#include "uart.h"#include "2440addr.h"#include "iic_lhg.h"#define U32 unsigned int#define devaddr 0xa0//设置IIC时钟,PCLK=50M,IICCLK=50M/16,Tx CLOCK=IICCLK/11=284.09KHZ#define rIICCON_init (1

    时间:2018-09-24 关键词: at24c02a iic

  • mini2440硬件篇之IIC

    1.硬件原理I2C总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。1.1.数据位的有效性规定I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。1.2.起始和终止信号SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。1.3.数据传送a、主机向从机发送数据,数据传送方向在整个传送过程中不变:b、主机在第一个字节后,立即从从机读数据c、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。2.芯片手册2.1.ATMEL公司的AT24C系列AT24C01:128字节(128×8位);AT24C02:256字节(256×8位);AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位);AT24C16:2K字节(2K×8位);AT24C系列E2PROM芯片地址的固定部分为1010,A2、A1、A0引脚接高、低电平后得到确定的3位编码。形成的7位编码即为该器件的地址码。2.2.写入单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。传送数据时,单片机首先发送一个字节的被写入器件的存储区的首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1,在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式:2.3.读出单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。3.mini2440电路图由原理图可以看出,2440只有两根引脚和EEPROM连接。4.S3C2440寄存器IICCON控制寄存器,控制是否发生ACK信号,设置发送器时钟,开启IIC中断。IICSTAT控制/状态寄存器,选择工作模式(从机接收、从机发生、主机接收、主机发送),发出S信号、P信号,使能接收/发送功能,并标识各种状态。IICADDR从机地址IICDS发送/接收寄存器,数据移位。IICLC行控制寄存器iic.h/********************************************************************Copyright(C),2011-2012,XXX.*FileName:iic.h*Author:HuangYinqing*Version:1.0*Date::2012-04-22*Description:IIC读写EEPROM.*FunctionList:*History:******************************************************************/#ifndef__IIC_H__#define__IIC_H__#defineDBG_IIC_LEVEL1#defineDEV_ADDR0xa0/*函数声明*/voidIICInit(void);voidIICTest(void);#endif//__IIC_H__iic.c/********************************************************************Copyright(C),2011-2012,XXX.*FileName:iic.c*Author:HuangYinqing*Version:1.0*Date::2012-04-22*Description:IIC驱动(中断方式).*FunctionList:*History:******************************************************************/#include"common.h"#include"core.h"#include"iic.h"unsignedcharpucIICBuffer[32][8];//IIC数据通讯缓存数组unsignedcharg_bIICFlag=0;//应答标志/********************************************************************函数功能:IIC中断函数。入口参数:无。返回:无。备注:无。********************************************************************/staticvoid__irqIICIntHandler(void){rSRCPND=BIT_IIC;//ClearpendingbitrINTPND=BIT_IIC;g_bIICFlag=1;}/********************************************************************函数功能:IIC初始化。入口参数:无。返回:无。备注:无。********************************************************************/voidIICInit(void){rGPECON&=0x0fffffff;rGPECON|=0xa0000000;//GPE15:IICSDA,GPE14:IICSCLrGPEUP|=3

    时间:2018-09-19 关键词: mini2440 硬件篇 iic

首页  上一页  1 2 3 4 5 下一页 尾页
发布文章

技术子站

更多

项目外包