nrf51822裸机教程-IIC
关于IIC总线的核心有以下几点:
:时钟线高电平期间必须保持数据线不变。
:时钟线低电平期间可以改变数据。
:时钟线和数据线上都要接上拉电阻,以使总线不工作时,两根线的电平都处于高电平状态。
:每个传输的字节后面需要由对方回送一个应答信号。
由上面可知,在时钟线为高电平的时候如果数据线改变,那么就是”不合法” 的。于是就刚好利用这种”不合法的”的跳变来作为数据 起始信号和停止信号。
于是规定:
:时钟线为高电平时,数据线由高到低跳变为起始信号
:时钟线为高电平时,数据线有低到高跳变为 停止信号。
关于IIC的原理和时序,网上很多文章,这里主要介绍 51822 硬件Iic的使用。
首先看先相关寄存器的说明:
STARTRX: 启动接收,即iic的读。
STARTTX:启动发送,即写。
ADDRESS: 设备地址寄存器,IIC 总线的通信,总是以地址+读写标识 开始,因为总线上可能挂了不止一个IIC设备,所以需要通过发送地址来说明要和哪个设备通信。
这里需要注意的是,51822的ADRESS寄存器只有7位有效,不包含低8位的 读写指示,读写指示是 硬件通过 你是启动读(通过设置STARTRX寄存器)还是启动写(通过设置STARTTX寄存器)来自动 在ADDRESS的7位发送完后在发送的。
所以在使用时,你不需要自己根据是读还是写,而设置地址寄存器ADDRESS =( ADDRESS<<8) |0x01 或ADDRESS =( ADDRESS<<8)&0xfe。 而是 直接ADDRESS = 7位设备地址 就可以了。读写位 会有硬件自动发送。
STOP:停止IIC
SUSPEND:挂起IIC(暂停),通常在 IIC读中使用,是为了在收到一个字节后,暂停IIC的传输,以保证 接收数据寄存器不被后续的数据覆盖。
RESUME:恢复被暂停的IIC,继续传输
关于事件寄存器,主要是如下两个事件需要关注:
RXDREADY: 指示数据接收完成。
TXDREADY:指示数据发送完成
BB:该事件在每一个字节发送或者接收之前产生,改事件通常使用在 读操作中,即接收操作。
SHORTS:该寄存器重要用来 将某个event和task短接。上面 说过,通过 设置SUSPEND可以暂停IIC总线,这样可以避免后续的接收数据覆盖了接收寄存器中的数据,而BB事件在每次数据接收之前会产生。 于是在接收过程中,可以通过判断接收的数据量如果还大于1那么久应该 通过SHORTS寄存器将BB事件和SUSPEND任务短接,那么每次从接收寄存器RXD中提取数据时,IIC总线就会自动被暂停,也就避免了后续数据覆盖了RXD内容,而如果接收的数据只剩最后一个了,那么久可以将BB事件和STOP 任务短接,那么在接收最后一个数据后就会自动发送停止信号了。这块看后面的代码注释更好理解。
INTEN:
INTENSET:
INTENCLR:
以上三个寄存器都是用来设置 当产生各种事件是是否产生中断。本教程中并未使用中断。
ERRORSRC:用来记录产生的错误原因
PSELSCL:用来选择哪个引脚作为 时钟线
PSELSDA:用来选择哪个引脚作为数据线
RXD:从该寄存器中提取接收到的数据
TXD:将要发送的数据填入该寄存器
FREQUENCY:设置 发送速率
ADDRESS:设置要通信的设备的地址
51822的IIC写操作如下图所以所示:
所以对于写需要如下几个步骤:
1:首先设置地址寄存器。
2:设置 STARTTX启动写操作。
3:将要发送的数据放入TXD寄存器中。
4:等待TXDSENT信号
5:如果有数据,继续将后续数据放入TXD中,并会到步骤4.否则到步骤6
6:设置STOP寄存器,并等待IIC停止了。
对于读操作,一般IIC设备都需要先提供要读的寄存器或地址。所以读操作一般需要先有一个写操作,来设置要读的地址或寄存器,然后再跟随读操作。51822提供的操作图如下所示:
所以对于需要先写地址再执行读的操作有如下几个步骤:
1:设置设备地址
2:设置STARTTX启动写操作
3:将要发送的数据(寄存器地址或数据地址)写入TXD寄存器中
4:等待TXDSENT 事件,以确定数据发送完毕。
5:判断是否只有一个要读的数据,如果不是设置 SHORTS将BB event和SUSPEND task短接(BB evnet 产生时自动触发SUSPEND task),否设置则BB event和STOP task短接。
6:设置STARTRX寄存器启动读操作。
7:等待RXDRDY事件,提取数据。如果后续只有一个要读的数据了,则设置BB event和STOP task短接,并跳到8。否则继续执行7
8:等待STOPED信号。
=下面介绍main.c代码细节。我的板子上有一个MPU6050是通过IIC来操作的,所以这里就使用该设备来验证IIC驱动的正确性。 根据板子的接线原理图,6050的设备地址为0x69,根据6050的手册知道该传感器 0x75地址的寄存器中存放的数据始终未0x68,所以下面就读这个寄存器中的值,来验证IIC是否正确通信,分别用两个led来指示读取的数据是否正确
PS:下面的驱动只是为了说明驱动原理,错误情况处理以及等待超时都没有做,如果自己的项目中需要使用IIC,请使用sdk中提供的,或者将下面的驱动参考SDK中的驱动加上错误处理和超时处理的相关代码。
#include "nrf51.h" #include "nrf_gpio.h" #include <stdbool.h> #include <stdio.h> #include <stdint.h> #include "nrf_delay.h" #define SCL_PIN (1) #define SDA_PIN (5) void iic_init(void){ NRF_TWI0->PSELSCL = SCL_PIN; NRF_TWI0->PSELSDA = SDA_PIN; NRF_TWI0->FREQUENCY = 0x06680000;//400Khz NRF_TWI0->ENABLE = ; //清零各种事件 NRF_TWI0->EVENTS_STOPPED = ; NRF_TWI0->EVENTS_RXDREADY = ; NRF_TWI0->EVENTS_TXDSENT = ; NRF_TWI0->EVENTS_BB = ; NRF_TWI0->EVENTS_ERROR = ; } void write_datas(uint8_t dev_addr, uint8_t arg_addr, uint32_t len, uint8_t *p_data, bool is_stop){ NRF_TWI0->ADDRESS = dev_addr; NRF_TWI0->TXD = arg_addr; NRF_TWI0->EVENTS_TXDSENT = ; //先清零一下事件 NRF_TWI0->TASKS_STARTTX = 0X01; //开始启动发送 ){ ){ } //等待发送完成 NRF_TWI0->EVENTS_TXDSENT = ; //清零 ){ break; } NRF_TWI0->TXD = *p_data++; } //判断是否需要停止IIC,对于单独的写操作,应该需要停止IIC, //对于读寄存器中值的操作,因为需要先写地址,后再发起读,所以前面的写操//作之后就不需要 停止IIC。 if ( is_stop ){ NRF_TWI0->EVENTS_STOPPED = ; NRF_TWI0->TASKS_STOP = ; ){}; //等待iic正确结束 } } void read_data(uint8_t dev_addr, uint8_t arg_addr, uint32_t len, uint8_t *p_data){ write_datas(dev_addr, arg_addr, , NULL, false); //先写地址 // NRF_TWI0->ADDRESS = dev_addr; //设置地址 ){ NRF_TWI0->SHORTS = ; }else{ NRF_TWI0->SHORTS = ; //将BB事件和SUSPEND短接,则每次接收到数据后,总线被挂起,目的是为了在提取数据的时候,防止对方又发送数据过来导致接收的数据被覆盖 } NRF_TWI0->EVENTS_RXDREADY = ; //先清零一下事件 NRF_TWI0->EVENTS_STOPPED = ; NRF_TWI0->TASKS_STARTRX = 0X01; //开始启动接收 ){ ){} //等待接收到数据 NRF_TWI0->EVENTS_RXDREADY = ; //清零事件 *p_data++ = NRF_TWI0->RXD; ){ break; } ){ NRF_TWI0->SHORTS = ; } NRF_TWI0->TASKS_RESUME = ; } ){} } #define LED1 (18) #define LED2 (19) int main(void){ uint8_t who_am_i; //我的板子 是高电平点亮LED,这里是置低关led nrf_gpio_cfg_output(LED1); nrf_gpio_pin_clear(LED1); nrf_gpio_cfg_output(LED2); nrf_gpio_pin_clear(LED2); nrf_delay_ms(); iic_init(); read_data(); if(who_am_i == 0x68){ nrf_gpio_pin_set(LED1); }else{ nrf_gpio_pin_set(LED2); } ); ; }
nrf51822裸机教程-IIC的更多相关文章
- nrf51822裸机教程-SPI(主)
关于SPI总线的介绍这里就不细说了,网上有很多介绍SPI总线时序的. SPI总线的本质就是一个环形总线结构,在时钟驱动下两个双向移位寄存器进行数据交换. 所以SPI总线的特色就是:传输一字节数据的同时 ...
- nrf51822裸机教程-UART
art硬件模块通常都有内置的硬件接收buff,比如51822的硬件uart模块图如下 因为通常接收到uart数据时都会做一些处理.比如保存到数据,或者对数据做一些判断之类的. 如果uart的波特率设置 ...
- nrf51822裸机教程-RTC
RTC0被协议栈使用了.所以在跑蓝牙程序的情况下.RTC0不能使用. RTC相关寄存器如下: EVTEN,EVTENSET,EVTENCLR. 这三个寄存器用来设置是否使能某个事件.(TICK,OVR ...
- nrf51822裸机教程-PWM
先简单介绍一下PWM的原理. 原理很简单. 假设COUNTER是个从0开始递增的计数器. 我们设置两个值 counter0 和counter1 在 COUNTER 计数到counter0的值时候翻转 ...
- nrf51822裸机教程-PPI
Programmable Peripheral Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性. 目的是为了让51822 的外围模块可以不通过处理器而自动相互作用. ...
- nrf51822裸机教程-GPIOTE
GPIO通常都会具有中断功能,上一讲的GPIO中并没有涉及到中断的相关寄存器. 51822将GPIO的中断相关做成了一个单独的模块GPIOTE,这个模块不仅提供了GPIO的中断功能,同时提供了 通过t ...
- nrf51822裸机教程-硬件timer
该讲介绍51822的Timer/Counter模块工作在timer模式下(定时器模式,还可以工作为计数器模式) 如何操作 51822的Timer/Counter结构如下图所示 Timer模块从PCLK ...
- nrf51822裸机教程-GPIO
首先看看一下相关的寄存器说明 Out寄存器 输出设置寄存器 每个比特按顺序对应每个引脚,bit0对应的就是 引脚0 该寄存器用来设置 引脚作为输出的时候的 输出电平为高还是低. 与输出设置相关的 还有 ...
- nRF51822之模拟IIC
使用的工程为是基于sdk10工程 在将以nRF51_SDK_10.0.0_dc26b5e\examples\peripheral\twi_sensor作为模版 修改代码main.c #include ...
随机推荐
- Linux下双网卡绑定(bonding技术)
Linux网卡绑定探析 2013-08-20 15:39:31 现在很多服务器都自带双千兆网口,利用网卡绑定既能增加网络带宽,同时又能做相应的冗余,目前应用于很多的场景.linux操作系统下自带的 ...
- Unicode编码
Unicode为世界上所有的文字系统的每一个字符单位分配了一个唯一的整数,称为代码点,范围为:0~1114111: ASCII将每一索引映射为唯一的二进制表示,但Unicode允许多个不同二进制编码的 ...
- json学习系列(3)-JSONObject的过滤设置
我们通常对一个json串和java对象进行互转时,经常会有选择性的过滤掉一些属性值.例如下面的实体类: package com.pcitc.json; /** * Person实体类 * * @Des ...
- MVC 依赖注入扩展
需求: 小明想要完成一个功能F,需要一把锤子T. 有两种办法可以实现: 1)小明很爱动手,精力很旺盛,于是,自己创建一个具有功能F的锤子T,并使用T来完成F: 2)小明很懒,天天睡大觉,于是,他叫小健 ...
- POJ2115 C Looooops(线性同余方程)
无符号k位数溢出就相当于mod 2k,然后设循环x次A等于B,就可以列出方程: $$ Cx+A \equiv B \pmod {2^k} $$ $$ Cx \equiv B-A \pmod {2^k} ...
- Revit二次开发示例:HelloRevit
本示例实现Revit和Revit打开的文件的相关信息. #region Namespaces using System; using System.Collections.Generic; using ...
- CodeForces Round 196
Div2-A 题意:有m个拼图,每个拼图有f[i]块.从中选出n个,使得 (其中块数最大减块数最小的值) 最小.思路:把f按从小到大的顺序排序,然后顺次尝试. #include<stdio.h& ...
- lua ipairs
tbl = {"alpha", "beta", ["one"] = "uno", ["two"] = ...
- POJ 1625 Censored!(大数+DP)
题目链接 这题,真心木啥意思,就是数据里貌似字符有负数,注意gets读入.. #include <iostream> #include <cstring> #include & ...
- 对于String对象,可以使用"="赋值,也可以使用"new"关键字赋值,两种方式有什么区别?
当你看见这个标题的时候,你可能会下意识的去想一下,这两种方式到底有什么样的区别呢? 且看下面的demo,自然便区分开了 /** * */ package com.b510.test; /** * Pr ...