目录
1.12C接口简介
2.12C接口通信2.1主机通信流程
2.1.1 主机通信初始化
1>主机时钟初始化
2>主机通信初始化
3>主机 10 bits 寻址的特殊时序初始化
2.1.2 主机通信初始化软件接口
2.1.3 主机发送流程
2.1.4 主机发送流程软件接口
2.1.5 主机接收流程
2.1.6 主机接收流程软件接口2.2 从机通信流程
2.2.1 从机通信初始化
1>从机地址配置
2>从机地址匹配
3>从机字节控制模式(通常SMBus 模式下才使用)
2.2.2 从机通信初始化软件接口
2.2.3 从机发送流程
2.2.4 从机发送流程软件接口
2.2.5 从机接收流程
2.2.6 从机接收流程软件接口2.3 唤醒深睡眠模式
2.4 IIC读写EEPROM
2.4.1 功能简介
2.4.2 资源准备
2.4.3 软件设计
引言
AT32 的 I2C 总线接口用于处理微控制器和串行 I2C 总线之间的通信,支持主机和从机模式,支持唤醒深睡眠模式,最大通信速度为 1Mbit/s(增强快速模式 fast mode plus)。
本文主要就 I 2C 总线接口的基本功能进行讲解和案列解析。
1. I2C接口简介
I2C 接口是由数据线 SDA 和时钟线 SCL 构成,在标准模式下通信速度可达到 100kHz,快速模式下则可以达到 400kHz,增强快速模式可达到 1MHz。
一帧数据传输从开始信号开始,在结束信号后停止,在收到开始信号后总线被认为是繁忙的,当收到结束信号后,总线被认为再次空闲。
I2C 接口具有主机和从机模式、多主机功能、可编程建立和保持时间、时钟延展功能、DMA 存取数据、支持SMBus 2.0 协议等特点。
I 2C1 可透过配置 CRM 中 PICLKS 寄存器的 I2C1SEL 位,时钟来源可选择来自 SYSCLK、PCLK和 HICK,并且支持从 Deepsleep mode 唤醒, I2C1 有模拟滤波器,可以滤掉 50ns 内的噪声。
I 2C2 / I 2C3 时钟来源为 PCLK,不支持 Deepsleep mode 唤醒并且没有模拟滤波器。
2. I2C接口通信
2.1主机通信流程
2.1.1 主机通信初始化
1> 主机时钟初始化
在启动外设(I2CEN)之前,必须先设置 I2Cx_CLKCTRL 寄存器的各个位用以配置 I 2C 主时钟。
― DIV[7:0]: I2C 时钟分频;
― SDAD[3:0]:数据保持时间(tHD;DAT)
― SCLD[3:0]:数据建立时间(tSU;DAT)
― SCLH[7:0]:SCL 高电平时间
― SCLL[7:0]:SCL 低电平时间
该寄存器的配置可以使用 Artery_I2C_Timing_Configuration 时钟配置工具计算,见第三部分。
- 低电平控制:
当检测到 SCL 总线为低电平时,内部 SCLL 计数器开始计数,当计数值达到 SCLL 值时,释放 SCL 线,SCL 线变为高电平。
- 高电平控制:
当检测到 SCL 总线为高电平时,内部 SCLH 计数器开始计数,当计数值达到 SCLH 值时,拉低 SCL 线,SCL 线变为低电平,当在高电平期间,如果被外部总线拉低,那么内部 SCLH 计数器停止计数,并开始低电平计数,这为时钟同步提供了条件。
2> 主机通信初始化
在启动通讯前须先设定 I2C_CTRL2 寄存器中的几项参数:
-
1)设置传输字节数
-
≤255 字节
配置 I2C_CTRL2 的 RLDEN=0,关闭重载模式 配置 I2C_CTRL2 的 CNT[7:0] = N
-
->255 字节
配置 I2C_CTRL2 的 RLDEN=1,使能重载模式 配置 I2C_CTRL2 的 CNT[7:0]=255 剩余传输字节数 N = N - 255
-
-
2)设置传输结束模式
-
软件结束模式
ASTOPEN=0:软件结束模式,当数据传输完成后,I2C_STS 的 TDC 标志置 1,软件设置GENSTOP=1 或者 GENSTART=1,发送 STOP 条件或者 START 条件。
-
自动结束模式
ASTOPEN=1:自动结束模式,当数据传输完成后,自动发送 STOP 条件。
-
-
3)设置从机地址
设置寻址的从机地址值(I2C_CTRL2 的 SADDR) 设置从机地址模式(I2C_CTRL2 的 ADDR10) ADDR10=0:7 位地址模式 ADDR10=1:10 位地址模式
-
4)设置传输方向(I2C_CTRL2 的 DIR)
DIR=0:主机接收数据 DIR=1:主机发送数据
-
5)开始传输
设置 I2C_CTRL2 的 GENSTART=1,主机开始在总线上**发送 START 条件和从机地址**。
3> 主机 10 bits 寻址的特殊时序初始化
在 10 位地址传输模式下,I2C_CTRL2 的 READH10 用于产生特殊时序,当 READH10=1 时,支持如下传输序:主机先发送数据给从机,然后再从从机读取数据,传输时序图如下图所示:
2.1.2 主机通信初始化软件接口
主机通信初始化所用到的软件接口通过独立的函数接口实现,如下:
/* 主机时钟初始化 */
void i2c_init(i2c_type *i2c_x, uint8_t dfilters, uint32_t clk);
/* 主机通信初始化 */
void i2c_transmit_set
(
i2c_type *i2c_x,
uint16_t address,
uint8_t cnt,
i2c_reload_stop_mode_type rld_stop,
i2c_start_stop_mode_type start_stop
);
/* 10 位地址使能 */
void i2c_addr10_mode_enable(i2c_type *i2c_x, confirm_state new_state);
/* 10 位地址头读取时序使能 */
void i2c_addr10_header_enable(i2c_type *i2c_x, confirm_state new_state);
i2c_init 函数
用于主机时钟初始化,三个参数分别为:所使用的 I2C、数字滤波值和主机时钟配置值。
i2c_transmit_set 函数
用于初始化通信参数,包括:所使用的 I2C、从机地址、传输字节数、停止条件产生模式和起始条件产生模式。
i2c_addr10_mode_enable 函数
用于使能 10 位地址模式。
i2c_addr10_header_enable 函数
用于使能 10 位地址头读取时序,即主机发送完整的 10 位从机地址读序列或主机只发送 10 位地址的前 7 位。
2.1.3 主机发送流程
-
I2C_TXDT 数据寄存器为空,I2C_STS 的 TDIS=1;
-
向 TXDT 数据寄存器写入数据,数据开始发送;
-
重复 1、2 步骤直到发送 CNT[7:0]个数据;
-
如果此时 I2C_STS 的 TCRLD=1(重载模式),分为以下两种情况:
― 剩余字节数 N>255: 向 CNT 写入 255,N=N-255,TCRLD 被自动清 0,传输继续; ― 剩余字节数 N≤255: 关闭重载模式(RLDEN=0),向 CNT 写入 N,TCRLD 被自动清 0,传输继续。
-
结束时序
― 停止条件产生:
软件结束模式(ASTOPEN=0):此时 I2C_STS 的 TDC 置 1,设置 GENSTOP=1产生 STOP 条件; 自动结束模式(ASTOPEN=1):自动产生 STOP 条件。
― 等待产生 STOP 条件:
当 STOP 条件产生时,I2C_STS 的 STOPF 置 1,将 I2C_CLR的 STOPC 写 1,清除 STOPF 标志,传输结束.
2.1.4 主机发送流程软件接口
主机发送通过独立的函数接口实现,如下:
i2c_status_type i2c_master_transmit
(
i2c_handle_type* hi2c,
uint16_t address,
uint8_t* pdata,
uint16_t size,
uint32_t timeout
);
i2c_master_transmit 函数为 i2c_application.c 文件所提供的应用层接口函数,参数包括:I2C 结构体指针、从机地址、发送数据指针、发送数据字节数和函数超时时间。
注:此函数为Artery 所提供的标准主机发送函数。用户也可根据前述主机发送流程,自行编写主机发送函数。
2.1.5 主机接收流程
-
当收到数据后,RDBF=1,读取 RXDT 数据寄存器,RDBF 被自动清零;
-
重复步骤 2 直到接收 CNT[7:0]个数据;
-
如果此时 I2C_STS 的 TCRLD=1(重载模式),分为以下两种情况:
― 剩余字节数 N>255:向 CNT 写入 255,N=N-255,TCRLD 被自动清 0,传输继续; ― 剩余字节数 N≤255:关闭重载模式(RLDEN=0),向 CNT 写入 N,TCRLD 被自动清 0,传输继续。
-
当在接收到最后一个字节时,主机会自动发送一个 NACK。
-
结束时序
― 停止条件产生: 软件结束模式(ASTOPEN=0):此时 I2C_STS 的 TDC 置 1,设置 GENSTOP=1 产生STOP 条件; 自动结束模式(ASTOPEN=1):自动产生 STOP 条件。 ― 等待产生 STOP 条件: 当 STOP 条件产生时,I2C_STS 的 STOPF 置 1,将 I2C_CLR 的STOPC 写 1,清除 STOPF 标志,传输结束。
2.1.6 主机接收流程软件接口
主机接收通过独立的函数接口实现,如下:
i2c_status_type i2c_master_receive
(
i2c_handle_type* hi2c,
uint16_t address, uint8_t* pdata,
uint16_t size,
uint32_t timeout
);
i2c_master_receive 函数为 i2c_application.c 文件所提供的应用层接口函数,参数包括:I2C 结构体指针、从机地址、接收数据指针、接收数据字节数和函数超时时间。
注:此函数为 Artery 所提供的标准主机接收函数。用户也可根据前述主机接收流程,自行编写主机接收函数。
2.2 从机通信流程
2.2.1 从机通信初始化
1> 从机地址配置
每个 I 2C 从设备可同时支持 2 个从设备地址,由 OADDR1 和 OADDR2 指定
-
I2C_OADDR1
— 通过 ADDR1EN 使能 — 通过 ADDR1MODE 配置为 7 位(默认)或 10 位地址
-
I2C_OADDR2
— 通过 ADDR2EN 使能 — 固定 7 位地址模式 — 可通过 ADDR2MASK [2:0]来在进行地址匹配比较时屏蔽掉 0~7 个 LSB 地址位 ADDR2MASK = 0 表示 7 位地址中的每一位都要参与匹配比较 ADDR2MASK = 7 表示任何非保留地址的 7 位地址都会被该从设备应答
2> 从机地址匹配
当 I 2C 启用的地址选中匹配时,ADDRF 中断状态标志会被置 1,如果 ADDRIEN 位为 1,就会产生一个中断。
如果两个从地址都使能,在地址匹配产生 ADDR 中断时,可以查看状态寄存器中的ADDR [6:0]来得知是 OADDR1 还是 OADDR2 被寻址了。
3> 从机字节控制模式(通常 SMBus 模式下才使用)
从设备可以对每个收到的字节进行应答控制。
所需配置:SCTRL = 1 & RLDEN =1 & STRETCH = 0 & CNT ≥ 1
从机字节控制流程:
1) 每收到一个字节 TCRLD 置位,时钟延展于第 8 和第 9 个脉冲之间
2) 软件读取 RXDT 中的值,并决定是否置位 ACK
3) 软件重装载 CNT = 1 来停止时钟延展
4) 应答或非应答信号在第 9 个脉冲时刻出现在总线上
注意:
置位 SCTRL 时,必须开启时钟延展,即 STRETCH = 0;
CNT 可以是大于 1 的值,来实现多个字节以自动 ACK 接收完毕后再启动应答控制,从设备发送时推荐关闭 SCTRL,此时无需字节应答控制。
2.2.2 从机通信初始化软件接口
从机通信初始化所用到的软件接口通过独立的函数接口实现,如下:
void i2c_own_address1_set(i2c_type *i2c_x, i2c_address_mode_type mode, uint16_t address);
void i2c_own_address2_set(i2c_type *i2c_x, uint8_t address, i2c_addr2_mask_type mask);
void i2c_own_address2_enable(i2c_type *i2c_x, confirm_state new_state);
void i2c_slave_data_ctrl_enable(i2c_type *i2c_x, confirm_state new_state);
void i2c_clock_stretch_enable(i2c_type *i2c_x, confirm_state new_state);
void i2c_reload_enable(i2c_type *i2c_x, confirm_state new_state);
i2c_own_address1_set 函数用于配置 OADDR1 地址模式以及 ADDR1 地址值。
i2c_own_address2_set 函数用于配置 ADDR2 地址值以及 ADDR2 屏蔽位。
i2c_own_address2_enable 函数用于使能 ADDR2 地址。
i2c_slave_data_ctrl_enable 函数用于使能从机字节控制模式。
i2c_clock_stretch_enable 函数用于使能从机时钟延展功能。
i2c_reload_enable 函数用于使能发送数据重载模式。
2.2.3 从机发送流程
-
响应主机地址,匹配时回复 ACK;
-
TXDT 为空时,置位 TDIS,从设备写入发送数据;
-
每发送一个字节会收到 ACK,且置位 TDIS;
-
如果收到 NACK 位:
— 置位 NACKF,产生中断; — 从设备自动释放 SCL 和 SDA(以便主设备发送 STOP 或 RESTART);
-
如果收到 STOP 位:
— 置位 STOPF,产生中断;
当从机发送开启时钟延展(STRETCH = 0)时,在等待 ADDRF 标志时和发送前一个数据的第 9 个时钟脉冲后,会把 TXDT 中的数据拷贝到移位寄存器中,如果此时 TDIS 还是置位,表示 TXDT 没有写进待发送数据,将发生时钟延展,如下流程图:
需要注意的是,在时钟延展关闭(STRETCH=1)的情况下,如果在将要传输数据的第一个 Bit 位开始发送之前,也就是 SDA 边沿产生之前,如果数据还未写入 TXDT 数据寄存器,那么会发生欠载错误,此时 I2C_STS 的 OUF 将会置 1,并将 0xFF 发送到总线。
为了能及时的写入数据,可以在通信开始前,先将数据写入到 DT 寄存器:软件先将 TDBE 置 1,目的是为了清空 TXDT 寄存器的数据,然后将第一个数据写入 TXDT 寄存器,此时 TDBE 清零。
2.2.4 从机发送流程软件接口
从机发送通过独立的函数接口实现,如下:
i2c_status_type i2c_slave_transmit
(
i2c_handle_type* hi2c,
uint8_t* pdata,
uint16_t size,
uint32_t timeout
);
i2c_slave_transmit 函数为 i2c_application.c 文件所提供的应用层接口函数,参数包括:I2C 结构体指针、发送数据指针、发送数据字节数和函数超时时间。
注:此函数为 Artery 所提供的标准从机发送函数。用户也可根据前述从机发送流程,自行编写从机发送函数。
2.2.5 从机接收流程
-
当收到数据后,RDBF=1,读取 RXDT 数据寄存器,RDBF 被自动清零;
-
重复步骤 2 直到所有数据接收完成;
-
等待收到 STOP 条件,当收到 STOP 条件时,I2C_STS 的 STOPF 置 1,将 I2C_CLR的 STOPC 写 1,清除 STOPF 标志,传输结束。
2.2.6 从机接收流程软件接口
从机接收通过独立的函数接口实现,如下:
i2c_status_type i2c_slave_receive
(
i2c_handle_type* hi2c,
uint8_t* pdata,
uint16_t size,
uint32_t timeout
);
i2c_slave_receive 函数为 i2c_application.c 文件所提供的应用层接口函数,参数包括:I2C 结构体指针、接收数据指针、接收数据字节数和函数超时时间。
注:此函数为 Artery 所提供的标准从机接收函数。用户也可根据前述从机接收流程,自行编写从机接收函数。
2.3 唤醒深睡眠模式
A423 上有 3 个 I2C,其中只有 I2C1 支持在被寻址到时将系统从深睡眠模式(DEEPSLEEP)唤醒。
使用此功能的配置步骤:
-
使能 I2C 唤醒深睡眠模式功能(I2C1_CTRL1 的 WAKEUPEN 位置 1)
i2c_wakeup_enable(i2cx, TRUE);
-
数字滤波器值设置为 0(I2C1_CTRL1 的 DFLT 位设为 0)
i2c_init(i2cx, 0x00, I2Cx_CLKCTRL);
-
开启时钟延展模式(I2C1_CTRL1 的 STRETCH 位设为 0)
i2c_clock_stretch_enable(i2cx, TRUE);
-
I2C 时钟选择 HICK(CRM_MISC2 的 I2C1SEL 位)
crm_i2c1_clock_source_set(CRM_I2C1_CLOCK_SOURCE_HICK48);
关于 A423 唤醒深睡眠模式更多详细信息请参考《AN0208_AT32A423_PWC_Application_Note》
2.4 IIC读写EEPROM
4.1 功能简介
使用硬件 I 2C 接口对 EEPROM 存储设备进行读写访问。
4.2 资源准备
-
硬件环境:
对应产品型号的 AT-START BOARD 4.7K 上拉电阻 EEPROM 存储设备
-
软件环境
project\at_start_a4xx\examples\i2c\eeprom
4.3 软件设计
-
配置流程
开启 I2C 外设时钟 配置 I2C 所复用的 GPIO 配置 I2C 所用的 DMA 通道 使能 I2C 外设接口 写入 EEPROM 并读取写入的数据 比较读写数据内容是否正确
-
代码介绍 main.c
如若读写数据完全相同,则 LED3 会被点亮。
int main(void) { i2c_status_type i2c_status; /* 初始化系统时钟 */ system_clock_config(); /* 配置 NVIC 优先级组 */ nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); /* at-start board 初始化 */ at32_board_init(); hi2cx.i2cx = I2Cx_PORT; /* 配置 I2C */ i2c_config(&hi2cx); while(1) { /* wait for key USER_BUTTON press before starting the communication */ while(at32_button_press() != USER_BUTTON) { } /* 写数据到 EEPROM */ if((i2c_status = i2c_memory_write(&hi2cx, I2Cx_ADDRESS, 0, tx_buf1, BUF_SIZE, I2C_TIMEOUT)) != I2C_OK) { error_handler(i2c_status); } delay_ms(5); /* 读 EEPROM 数据 */ if((i2c_status = i2c_memory_read(&hi2cx, I2Cx_ADDRESS, 0, rx_buf1, BUF_SIZE, I2C_TIMEOUT)) != I2C_OK) { error_handler(i2c_status); } (省略部分代码,完整代码请查看 BSP) /* 等待通讯完成 */ if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK) { error_handler(i2c_status); } /* 比较读写数据 */ if((buffer_compare(tx_buf1, rx_buf1, BUF_SIZE) == 0) && (buffer_compare(tx_buf2, rx_buf2, BUF_SIZE) == 0) && (buffer_compare(tx_buf3, rx_buf3, BUF_SIZE) == 0)) { at32_led_on(LED3); } else { error_handler(i2c_status); } } } error_handler(i2c_status); } (省略部分代码,完整代码请查看 BSP) /* 等待通讯完成 */ if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK) { error_handler(i2c_status); } /* 比较读写数据 */ if((buffer_compare(tx_buf1, rx_buf1, BUF_SIZE) == 0) && (buffer_compare(tx_buf2, rx_buf2, BUF_SIZE) == 0) && (buffer_compare(tx_buf3, rx_buf3, BUF_SIZE) == 0)) { at32_led_on(LED3); } else { error_handler(i2c_status); } } }