目录

硬件IIC的主从中断在582的最新EVT中已支持。

对于IIC从机中断,例程中已封装好中断处理过程,用户调用app_i2c时,初始化中需要配置回调函数。

初始化的配置如下。

    struct i2c_slave_cb slave_user = {          //配置回调结构体
.on_receive = i2c_on_slave_receive_user,
.on_transmit = i2c_on_slave_transmit_user,
};
i2c_app_init(RxAdderss); //IIC硬件模块的初始化及软件标志的初始化 i2c_slave_cb_register(&slave_user); //注册回调

默认回调函数如下。

static void i2c_on_slave_transmit_user(uint8_t *data, uint8_t *len)
{
*len = sizeof(TxData);
memcpy(data, TxData, sizeof(TxData));
} static void i2c_on_slave_receive_user(uint8_t *data, uint8_t len)
{
PRINT("I2C slave receiver callback: received (");
for(int i = 0; i < len; i++) {
PRINT(" %#x", data[i]);
}
PRINT(" )\n");
}

若需要串口打印日志,可以在工程预编译中增加宏CONFIG_I2C_DEBUG。

接下来以注释的方式解析一下中断服务函数中做了哪些处理。

__INTERRUPT
__HIGH_CODE
void I2C_IRQHandler(void)
{
uint32_t event = I2C_GetLastEvent();
print_i2c_irq_sta(event); /* I2C Master */
if (event & (RB_I2C_MSL << 16)) { //判断为主机模式
if (event & RB_I2C_SB) { //判断主机模式已发送起始信号
/* Start condition sent, send address */
I2C_SendData(i2c_slave_addr_rw); //写从地址到数据寄存器后,SB位会自动清除
I2C_DBG("Master selected, send address\n");
} /* I2C Master transmitter */
if (event & (RB_I2C_TRA << 16)) { //判断是主机的发送模式
I2C_DBG("Master transmitter:\n");
/* Slave receiver acked address or sent bit */
if (event & (RB_I2C_ADDR | RB_I2C_BTF | RB_I2C_TxE | (RB_I2C_TRA << 16))) {
/* if there is data to send, send it, otherwise stop */
if (i2c_master_buffer_index < i2c_master_buffer_length) { //判断待发送数据还没发完
I2C_SendData(i2c_master_buffer[i2c_master_buffer_index++]);
I2C_DBG(" send (%#x)\n",
i2c_master_buffer[i2c_master_buffer_index - 1]);
} else {
if (i2c_send_stop) { //判断数据发完了,如果允许产生停止位则产生停止位
i2c_state = I2C_READY;
I2C_GenerateSTOP(ENABLE); //产生停止位
I2C_DBG(" send STOP\n");
} else {
i2c_in_repstart = true; //置标志表示即将重起始
/* we're gonna send the START, don't enable the interrupt. */
I2C_ITConfig(I2C_IT_BUF, DISABLE);
I2C_ITConfig(I2C_IT_EVT, DISABLE);
I2C_ITConfig(I2C_IT_ERR, DISABLE);
I2C_GenerateSTART(ENABLE);
i2c_state = I2C_READY;
I2C_DBG(" restart\n");
}
}
} /* Address or data sent, nack received */
if (event & RB_I2C_AF) { //地址/数据发送后应答失败
I2C_ClearFlag(I2C_FLAG_AF); //清标志 i2c_error = I2C_MT_NACK; //置主机发送接收错误标志
i2c_state = I2C_READY; //发送停止信号停止帧传输
I2C_GenerateSTOP(ENABLE); //产生停止信号
I2C_DBG(" NACK received, sent stop\n");
}
} else { //判断为主机的接收模式
/* I2C Master reveiver */
I2C_DBG("Master receiver:\n"); /* address sent, ack received */
if(event & RB_I2C_ADDR) { //主机成功设置了从机的地址
/* ack if more bytes are expected, otherwise nack */
if (i2c_master_buffer_length) { //判断接下来需要接收多少字节,如果需要接收多个字节,就置自动回ACK
              //注意这里判断条件,在1.8的583EVT与之前的EVT不同,以这里的为准。
I2C_AcknowledgeConfig(ENABLE);
I2C_DBG(" address sent\n");
I2C_DBG(" ACK next\n");
} else { //判断接下来只接收一个字节,关闭自动回ACK即自动回NACK
//XXX: Should not delay too match before NACK
I2C_AcknowledgeConfig(DISABLE);
is_nack_sent = true; //置标志,数据接收时做具体处理
I2C_DBG(" address sent\n");
I2C_DBG(" NACK next\n");
}
} /* data reveived */
if (event & (RB_I2C_RxNE)) { //接收数据寄存器内非空,即收到数据
/* put byte into buffer */
i2c_master_buffer[i2c_master_buffer_index++] = I2C_ReceiveData(); //接收数据到buffer中 if (i2c_master_buffer_index < i2c_master_buffer_length) { //判断接下来是否为期望接收的最后一个字节
I2C_AcknowledgeConfig(ENABLE); //接下来不是最后一个字节,接收应答设置为自动回ACK
I2C_DBG(" ACK next\n");
} else {
//XXX: Should not delay too match before NACK
I2C_AcknowledgeConfig(DISABLE); //连续读倒数最后一个字节前将接收应答ACK关闭,即最后一个字节后自动回NACK
I2C_DBG(" NACK next\n"); if (is_nack_sent) { //如果已设置最后一个字节前置了接收应答NACK
is_nack_sent = false; //清标志
if (i2c_send_stop) {
I2C_GenerateSTOP(ENABLE);
i2c_state = I2C_READY;
I2C_DBG(" send STOP\n");
} else {
i2c_in_repstart = true; //写完了从机内的目标寄存器,发重起始信号
/* we're gonna send the START, don't enable the interrupt. */
I2C_ITConfig(I2C_IT_BUF, DISABLE);
I2C_ITConfig(I2C_IT_EVT, DISABLE);
I2C_ITConfig(I2C_IT_ERR, DISABLE);
I2C_GenerateSTART(ENABLE);
i2c_state = I2C_READY;
I2C_DBG(" restart\n");
}
} else {
is_nack_sent = true; //没有要求
}
} I2C_DBG(" received data (%#x)\n",
i2c_master_buffer[i2c_master_buffer_index - 1]);
} /* nack received */
if (event & RB_I2C_AF) { //主机接收模式最后一个字节后为NACK
I2C_ClearFlag(I2C_FLAG_AF);
/* put final byte into buffer */
i2c_master_buffer[i2c_master_buffer_index++] = I2C_ReceiveData(); //收下最后一个字节的数据 if (i2c_send_stop) { //判断读完之后是停止信号还是重起始信号
i2c_state = I2C_READY;
I2C_GenerateSTOP(ENABLE);
I2C_DBG(" NACK received, send STOP\n");
} else {
i2c_in_repstart = true; //读完后不停止,会产生重起始信号以衔接后续操作
/* we're gonna send the START, don't enable the interrupt. */
I2C_ITConfig(I2C_IT_BUF, DISABLE);
I2C_ITConfig(I2C_IT_EVT, DISABLE);
I2C_ITConfig(I2C_IT_ERR, DISABLE);
I2C_GenerateSTART(ENABLE);
i2c_state = I2C_READY;
I2C_DBG(" restart\n");
}
}
} } else { //判断为从机模式
/* I2C slave */
/* addressed, returned ack */
if (event & RB_I2C_ADDR) { //地址匹配,接下来判断方向 if (event & ((RB_I2C_TRA << 16) | RB_I2C_TxE)) { //判断从机发送方向匹配或者发送方向寄存器空,那么接下来需要发送数据 //发送方向寄存器空需要判断吗?
I2C_DBG("Slave transmitter address matched\n"); i2c_state = I2C_STX;
i2c_slave_txbuffer_index = 0;
i2c_slave_txbuffer_length = 0; if (slave_cb && slave_cb->on_transmit) { //如果注册了回调,就按照回调函数,将数据拷贝到i2c_slave_txbuffer中
slave_cb->on_transmit(i2c_slave_txbuffer, &i2c_slave_txbuffer_length);
}
} else { //判断从机接收方向地址匹配,那么接下来需要接收数据
I2C_DBG("Slave reveiver address matched\n"); i2c_state = I2C_SRX;
i2c_slave_rxbuffer_index = 0;
}
} if (event & (RB_I2C_TRA << 16)) { //从机发送
/* Slave transmintter */
I2C_AcknowledgeConfig(ENABLE); //预设自动回复ACK
I2C_DBG("Slave transmitter:\n"); if (event & RB_I2C_AF) { //收到了NACK,发送失败
/* Nack received */
I2C_ClearFlag(I2C_FLAG_AF); //清除应答失败标志
I2C_AcknowledgeConfig(ENABLE); //预设自动回复ACK
I2C_DBG(" Nack received\n"); /* leave slave receiver state */
i2c_state = I2C_READY;
/* clear status */
event = 0;
} if(event & (RB_I2C_BTF | RB_I2C_TxE)) { //字节发送结束或者发送方向寄存器空
/* copy data to output register */ I2C_SendData(i2c_slave_txbuffer[i2c_slave_txbuffer_index++]); //逐字节发送数据 /* if there is more to send, ack, otherwise nack */
if (i2c_slave_txbuffer_index < i2c_slave_txbuffer_length) {
I2C_AcknowledgeConfig(ENABLE); //预设自动回复ACK
}else{
I2C_AcknowledgeConfig(DISABLE); //预设自动回复NACK
}
I2C_DBG(" send (%#x)\n",
i2c_slave_txbuffer[i2c_slave_txbuffer_index - 1]);
}
} else { //从机接收
/* Slave receiver */
I2C_DBG("Slave receiver:\n"); if (event & RB_I2C_RxNE) { //判断非空即收到了数据
/* if there is still room in the rx buffer */
//判断还有没有缓存空间,如果仍有空间就接收数据并回ACK,没有更多空间了就回NACK
if (i2c_slave_rxbuffer_index < I2C_BUFFER_LENGTH) {
/* put byte in buffer and ack */
i2c_slave_rxbuffer[i2c_slave_rxbuffer_index++] = I2C_ReceiveData();
I2C_AcknowledgeConfig(ENABLE);
I2C_DBG(" received (%#x)\n",
i2c_slave_rxbuffer[i2c_slave_rxbuffer_index - 1]);
} else {
// otherwise nack
I2C_AcknowledgeConfig(DISABLE);
}
} if (event & RB_I2C_STOPF) { //从机接收模式下收到停止位,清标志,清数组下标索引
/* ack future responses and leave slave receiver state */ R16_I2C_CTRL1 |= RB_I2C_PE; //clear flag I2C_DBG(" reveive stop\n"); /* callback to user defined callback */
if (slave_cb && slave_cb->on_receive) {
slave_cb->on_receive(i2c_slave_rxbuffer, i2c_slave_rxbuffer_index);
}
/* since we submit rx buffer , we can reset it */
i2c_slave_rxbuffer_index = 0;
} if (event & RB_I2C_AF) { //如果接收模式中接收到了NACK,清标志,预设自动回复ACK
I2C_ClearFlag(I2C_FLAG_AF); /* ack future responses */
I2C_AcknowledgeConfig(ENABLE);
}
}
} if(event & RB_I2C_BERR){ //总线错误
I2C_ClearFlag(RB_I2C_BERR);
I2C_GenerateSTOP(ENABLE); i2c_error = I2C_BUS_ERROR;
I2C_DBG("RB_I2C_BERR\n");
} if(event & RB_I2C_ARLO){ //仲裁丢失错误
I2C_ClearFlag(RB_I2C_ARLO); i2c_error = I2C_ARB_LOST;
I2C_DBG("RB_I2C_ARLO\n");
} I2C_DBG("\n");
}

硬件IIC主从机中断代码注释解析的更多相关文章

  1. STM32F10x_硬件I2C主从通信(轮询发送,中断接收)

    Ⅰ.写在前面 关注我分享文章的朋友应该知道我在前面讲述过(软件.硬件)I2C主机控制从机EEPROM的例子.在I2C通信主机控制程序是比较常见的一种,可以说在实际项目中,很多应用都会使用到I2C通信. ...

  2. STM32 IIC双机通信—— HAL库硬件IIC版

    参考传送门 关于IIC的原理这里我就不多说了,网上有很多很好的解析,如果要看我个人对IIC的理解的话,可以点击查看,这里主要讲一下怎样利用STM32CubeMx实现IIC的通讯,经过个人实践,感觉HA ...

  3. Hash源码注释解析

    部分代码注释解析: 1 import java.io.IOException; 2 import java.io.InvalidObjectException; 3 import java.io.Se ...

  4. 硬件IIC驱动原理

    1.IIC物理层 IIC通信属于同步半双工通信,IIC总线由两根信号线组成.一根是数据线SDA,一根是时钟线SCL,时钟线只能由主机发送给从机,数据线可以双向进行通信,总线上可挂载多个设备,挂载数量受 ...

  5. S3C2440硬件IIC详解

    S3C2440A RISC微处理器可以支持一个多主控IIC 总线串行接口.一条专用串行数据线(SDA)和一条专用串行时钟线(SCL)传递连接到IIC总线的总线主控和外设之间的信息.SDA和SCL线都为 ...

  6. linux内核代码注释 赵炯 第三章引导启动程序

    linux内核代码注释 第三章引导启动程序 boot目录中的三个汇编代码文件   bootsect.s和setup.s采用近似intel的汇编语法,需要8086汇编器连接器as86和ld86 head ...

  7. Arduino IIC 主从设备连接通信

    目的:        实现Arduino主从设备之间的互相IIC通信,掌握IIC通信协议的使用方法. 器材: Arduino UNO R3 一块 Arduino Nano 三块 面包板   导线 3K ...

  8. STM32硬件IIC操作

    Stm32具有IIC接口,接口有以下主要特性 多主机功能:该模块既可做主设备也可做从设备 主设备功能 C地址检测 位/10位地址和广播呼叫 支持不同的通讯速度 状态标志: 发送器/接收器模式标志 字节 ...

  9. STM32硬件IIC

    /** * @brief 写一个字节到I2C设备中 * @param * @arg pBuffer:缓冲区指针 * @arg WriteAddr:写地址 * @retval 正常返回1,异常返回0 * ...

  10. 菜鸟笔记 -- Chapter 4.7 代码注释与编码规范

    4.7 代码注释与编码规范 在程序代码中适当的添加注释可以提高程序的可读性和可维护性.好的编码规范可以使程序更易阅读和理解.下面我们将介绍几种代码注释,以及应该注意的编码规范. 4.7.1 代码注释 ...

随机推荐

  1. 小项目中vuex使用频率不太多我们完全可以用provide inject 来代替可以让项目小不少

    在一般小型项目中vuex实在是太浪费了所以我们可以用到 vue中的provide inject 代替 1.在vue3中我们先另起一个 文件创建一个全局的状态和方法的地方(如果你的全局状态特别的多记得要 ...

  2. jupyter的配置

    step1.安装jupyter 使用pip或者conda等包管理工具安装jupyter(这部分倒是没有任何难度,一般也没有什么坑) conda install jupyter notebook或者pi ...

  3. flutter报错The type of the function literal can't be inferred because the literal has a block as its body.A value of type 'String?' can't be assigned to a variable of type 'String'.

    flutter有一些报错如下 The type of the function literal can't be inferred because the literal has a block as ...

  4. QGraphicsView, QGraphicsObject ,QQGraphicsSvgItem 图片接收鼠标事件 拖拉 收放

    由于项目要求,需要加载svg格式图片和pixmap图片,并根据指定坐标在图上进行勾画,并且对相应位置接收鼠标事件. -继承QGraphicsObject,实现加载pixmap的项 myimageite ...

  5. 12月19日内容总结——Q查询进阶、ORM查询优化、ORM事务、ORM常用字段类型和字段参数、Ajax介绍、数据编码格式、Ajax携带文件数据

    目录 一.Q查询进阶操作 二.ORM查询优化 三.ORM事务操作 四.ORM常用字段类型 五.ORM常用字段参数 六.Ajax AJAX简介 应用场景 AJAX的优点 语法实现 七.数据编码格式(Co ...

  6. ASP.NET Core - IStartupFilter 与 IHostingStartup

    1. IStartupFilter   上面讲到的方式虽然能够根据不同环境将Startup中的启动逻辑进行分离,但是有些时候我们还会可以根据应用中的功能点将将一系列相关中间件的注册封装到一起,从 St ...

  7. java基础(完整版)

    java javaSE 注释: block块级 line 行 标识符和关键字 组成部分.变量名.类名.方法名--------标识符 $\数字._\字母 数据类型 强类型语言---安全性高--速度会慢 ...

  8. 10分钟学会使用 Loki 日志聚合系统

    Loki 是一个由Grafana Labs 开发的开源日志聚合系统,旨在为云原生架构提供高效的日志处理解决方案. Loki 通过使用类似 Prometheus 的标签索引机制来存储和查询日志数据,这使 ...

  9. swiper列数 slidesPerView属性决定

    swiper轮播一行有几列如下决定 slidesPerView为3是三列,不写一列 // 初始化文章swiper var newsSwiper = new Swiper('#news-swiper', ...

  10. WPF HandyOrg DataGrid 表格内容和标题居中显示

    表格内容居中 对于文本显示列DataGridTextColumn需要设定文本内容水平居中或者水平居右,而不是HandyControl中设定的样式默认显示为居左时,需要继承DataGridCellSty ...