【AT-START-F425测评】解读I2C i2c_application.c
[复制链接]
【前言】 准备使用AT32F425的i2c外设,查看AT32F425_Finrmware_Library_V2.0.1下面的i2c_apllication_library下的对i2c进行了中间件的封装,我是第一次看到官方对i2c进行二次封装的程序,但是他的注释都是英文的,这本着入门学习的着度来解读一下这个文档。
【目的】通过这个文档的学习,通过使用中间件的使用来驱动AH10温湿度计。
i2c_application.h
/** @defgroup I2C_library_event_check_flag //定义事件检查标志
* @{
*/
#define I2C_EVENT_CHECK_NONE ((uint32_t)0x00000000) /*!<不进行标志检查 */
#define I2C_EVENT_CHECK_ACKFAIL ((uint32_t)0x00000001) /*!< 检查ACK失败 */
#define I2C_EVENT_CHECK_STOP ((uint32_t)0x00000002) /*!< 检查STOP信号 */
/** @defgroup I2C_library_transmission_mode //工作的模式 定义了主从发送与接收模式,DMA主机发送与接收模式
* @{
*/
typedef enum
{
I2C_INT_MA_TX = 0,
I2C_INT_MA_RX,
I2C_INT_SLA_TX,
I2C_INT_SLA_RX,
I2C_DMA_MA_TX,
I2C_DMA_MA_RX,
I2C_DMA_SLA_TX,
I2C_DMA_SLA_RX,
} i2c_mode_type;
/** @defgroup I2C_library_status_code //工作状态模式 定义了正与错误的状态码,
* @{
*/
typedef enum
{
I2C_OK = 0, /*!< no error */
I2C_ERR_STEP_1, /*!< step 1 error */
I2C_ERR_STEP_2, /*!< step 2 error */
I2C_ERR_STEP_3, /*!< step 3 error */
I2C_ERR_STEP_4, /*!< step 4 error */
I2C_ERR_STEP_5, /*!< step 5 error */
I2C_ERR_STEP_6, /*!< step 6 error */
I2C_ERR_STEP_7, /*!< step 7 error */
I2C_ERR_STEP_8, /*!< step 8 error */
I2C_ERR_STEP_9, /*!< step 9 error */
I2C_ERR_STEP_10, /*!< step 10 error */
I2C_ERR_STEP_11, /*!< step 11 error */
I2C_ERR_STEP_12, /*!< step 12 error */
I2C_ERR_TCRLD, /*!< tcrld error */
I2C_ERR_TDC, /*!< tdc error */
I2C_ERR_ADDR, /*!< addr error */
I2C_ERR_STOP, /*!< stop error */
I2C_ERR_ACKFAIL, /*!< ackfail error */
I2C_ERR_TIMEOUT, /*!< timeout error */
I2C_ERR_INTERRUPT, /*!< interrupt error */
} i2c_status_type;
/** @defgroup I2C_library_handler
* @{
*/
typedef struct
{
i2c_type *i2cx; /*!< i2c registers base address i2c寄存器地址 */
uint8_t *pbuff; /*!< pointer to i2c transfer buffer 传输数据的数据指针 */
__IO uint16_t psize; /*!< i2c transfer size 需要传输的数据大小 */
__IO uint16_t pcount; /*!< i2c transfer counter 传输计数 */
__IO uint32_t mode; /*!< i2c communication mode 传输工作模式 */
__IO uint32_t status; /*!< i2c communication status 通信状态 */
__IO i2c_status_type error_code; /*!< i2c error code 错误代码 */
dma_channel_type *dma_tx_channel; /*!< dma transmit channel DMA发送传输通道 */
dma_channel_type *dma_rx_channel; /*!< dma receive channel DMA接收传输通道 */
dma_init_type dma_init_struct; /*!< dma init parameters DMA初始参数状态 */
} i2c_handle_type; //i2c句柄类型
《i2c_application.c》
/**
* @brief get the dma transfer direction flag through the channel 通过通首获取DMA传输方向的标志
*/
#define DMA_GET_REQUEST(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)hi2c->dma_tx_channel)) ? I2C_DMA_REQUEST_TX : I2C_DMA_REQUEST_RX)
/**
* @brief get the dma transfer complete flag through the channel //通过通道获取DMA传输完成的标志
*/
#define DMA_GET_TC_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_FDT1_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_FDT2_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_FDT3_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_FDT4_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_FDT5_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_FDT6_FLAG : \
DMA1_FDT7_FLAG)
/**
* @brief get the dma half transfer flag through the channel 通过通道获取dma半传输标志
*/
#define DMA_GET_HT_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_HDT1_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_HDT2_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_HDT3_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_HDT4_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_HDT5_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_HDT6_FLAG : \
DMA1_HDT7_FLAG)
/**
* @brief get the dma transfer error flag through the channel 通过通道获取dma传输错误标志
*/
#define DMA_GET_TERR_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_DTERR1_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_DTERR2_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_DTERR3_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_DTERR4_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_DTERR5_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_DTERR6_FLAG : \
((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL7))? DMA1_DTERR7_FLAG : \
DMA1_DTERR7_FLAG)
/**
* @brief i2c transmission status 传输状态
*/
#define I2C_START 0
#define I2C_END 1
/**
其实函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。
加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数,。
* @brief initializes peripherals used by the i2c. 初始化i2c使用的外围设备。注意:这个函数在主函数或者其他的函数上要自己定义。
* @param none
* @retval none
*/
__WEAK void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
}
/**
* @brief i2c peripheral initialization. 设备初始化函数
* @param hi2c: the handle points to the operation information. 数参为:指向hi2c 结构体指针。
* @retval none.
*/
void i2c_config(i2c_handle_type* hi2c)
{
/* reset i2c peripheral 初始化外设 */
i2c_reset(hi2c->i2cx);
/* i2c peripheral initialization 初始化i2c 定义 i2c寄存器地址、 传输数据的数据指针、 需要传输的数据大小 等等 hi2c可以事先定义,也可以在i2c_lowlevel_init里面重新指定参数*/
i2c_lowlevel_init(hi2c);
/* i2c peripheral enable 使能i2c外设 */
i2c_enable(hi2c->i2cx, TRUE);
}
/**
* @brief refresh i2c register. 刷新i2c寄存器。
* @param hi2c: the handle points to the operation information. 参数为(i2c_handle_type结构指针 个人理解为替换了i2c外设或者是传输模式变换了,重定传输参数后重新配置参数
* @retval none.
*/
void i2c_refresh_txdt_register(i2c_handle_type* hi2c)
{
/* clear tdis flag */
if (i2c_flag_get(hi2c->i2cx, I2C_TDIS_FLAG) != RESET)
{
hi2c->i2cx->txdt = 0x00;
}
/* refresh txdt register*/
if (i2c_flag_get(hi2c->i2cx, I2C_TDBE_FLAG) == RESET)
{
hi2c->i2cx->sts_bit.tdbe = 1;
}
}
/**
* @brief reset ctrl2 register. 重置ctrl2寄存器。主机发送时,要先设置I2C_CTRL2_CNT = N。
* @param hi2c: the handle points to the operation information.
* @retval none.
*/
void i2c_reset_ctrl2_register(i2c_handle_type* hi2c)
{
hi2c->i2cx->ctrl2_bit.saddr = 0;
hi2c->i2cx->ctrl2_bit.readh10 = 0;
hi2c->i2cx->ctrl2_bit.cnt = 0;
hi2c->i2cx->ctrl2_bit.rlden = 0;
hi2c->i2cx->ctrl2_bit.dir = 0;
}
/**
* @brief wait for the transfer to end. 等待数据传输结果
* @param hi2c: the handle points to the operation information. 指向外设的的结构体
* @param timeout: maximum waiting time. 超时时间 如果用硬件I2C,如果不设置超时,就可以阻塞在等那里
* @retval i2c status. 返回状态
*/
i2c_status_type i2c_wait_end(i2c_handle_type* hi2c, uint32_t timeout)
{
while(hi2c->status != I2C_END)
{
/* check timeout */
if((timeout--) == 0)
{
return I2C_ERR_TIMEOUT;
}
}
if(hi2c->error_code != I2C_OK)
{
return hi2c->error_code;
}
return I2C_OK;
}
/**
* @brief wait for the flag。等待传输标志
* @param hi2c: the handle points to the operation information. 指向外设的结构体
* @param flag: flag to wait. 需要等待的标志
* @param status: status to wait. 需要等待的状态
* @param event_check: flag to check while waiting for the flag. 检测的事件标志,只能选择下三个标志
* parameter as following values:
* - I2C_EVENT_CHECK_NONE
* - I2C_EVENT_CHECK_ACKFAIL
* - I2C_EVENT_CHECK_STOP
* @param timeout: maximum waiting time. 超时时间
* @retval i2c status. 返回状态
*/
i2c_status_type i2c_wait_flag(i2c_handle_type* hi2c, uint32_t flag, flag_status status, uint32_t event_check, uint32_t timeout)
{
while(i2c_flag_get(hi2c->i2cx, flag) == status)
{
/* check the ack fail flag */
if(event_check & I2C_EVENT_CHECK_ACKFAIL)
{
if(hi2c->i2cx->sts & I2C_ACKFAIL_FLAG)
{
/* clear ack fail flag */
i2c_flag_clear(hi2c->i2cx, I2C_ACKFAIL_FLAG);
hi2c->error_code = I2C_ERR_ACKFAIL;
return I2C_ERR_ACKFAIL;
}
}
/* check the stop flag */
if(event_check & I2C_EVENT_CHECK_STOP)
{
if(hi2c->i2cx->sts & I2C_STOPF_FLAG)
{
/* clear stop flag */
i2c_flag_clear(hi2c->i2cx, I2C_STOPF_FLAG);
i2c_reset_ctrl2_register(hi2c);
hi2c->error_code = I2C_ERR_STOP;
return I2C_ERR_STOP;
}
}
/* check timeout */
if((timeout--) == 0)
{
hi2c->error_code = I2C_ERR_TIMEOUT;
return I2C_ERR_TIMEOUT;
}
}
return I2C_OK;
}
/**
* @brief dma transfer cofiguration. //DMA传输配置
* @param hi2c: the handle points to the operation information. 外设结构构
* @param dma_channelx: dma channel to be cofigured. dma传输通道
* @param pdata: data buffer. 传输数发送或接收地址
* @param size: data size. 数据长度
* @retval none.
*/
void i2c_dma_config(i2c_handle_type* hi2c, dma_channel_type* dma_channel, uint8_t* pdata, uint16_t size)
{
/* disable the dma channel */
dma_channel_enable(dma_channel, FALSE);
/* disable the transfer complete interrupt 先关dma传输中断*/
dma_interrupt_enable(dma_channel, DMA_FDT_INT, FALSE);
/* configure the dma channel with the buffer address and the buffer size 配置dma 通道、传输方向 、地址、等 */
hi2c->dma_init_struct.memory_base_addr = (uint32_t)pdata;
hi2c->dma_init_struct.direction = (dma_channel == hi2c->dma_tx_channel) ? DMA_DIR_MEMORY_TO_PERIPHERAL : DMA_DIR_PERIPHERAL_TO_MEMORY;
hi2c->dma_init_struct.peripheral_base_addr = (dma_channel == hi2c->dma_tx_channel) ? (uint32_t)&hi2c->i2cx->txdt : (uint32_t)&hi2c->i2cx->rxdt;
hi2c->dma_init_struct.buffer_size = (uint32_t)size;
dma_init(dma_channel, &hi2c->dma_init_struct);
/* enable the transfer complete interrupt 开启dma传输中断 */
dma_interrupt_enable(dma_channel, DMA_FDT_INT, TRUE);
/* enable the dma channel 使能dma 通道*/
dma_channel_enable(dma_channel, TRUE);
}
/**
* @brief start transfer in poll mode or interrupt mode. 在轮询模式或中断模式下启动传输
* @param hi2c: the handle points to the operation information. 外设结构体
* @param address: slave address. 从机地址
* @param start_stop: config gen start condition mode. 启动条件 只能选择下面三个参数
* parameter as following values:
* - I2C_WITHOUT_START: transfer data without start condition. 不需要起始信号
* - I2C_GEN_START_READ: read data and generate start.读取数据并启动START信号
* - I2C_GEN_START_WRITE: send data and generate start. 发送数据并启动START信号
* @retval i2c status. 返回状态
*/
void i2c_start_transfer(i2c_handle_type* hi2c, uint16_t address, i2c_start_stop_mode_type start_stop)
{
if (hi2c->pcount > MAX_TRANSFER_CNT)
{
hi2c->psize = MAX_TRANSFER_CNT;
i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_RELOAD_MODE, start_stop);
}
else
{
hi2c->psize = hi2c->pcount;
i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_AUTO_STOP_MODE, start_stop);
}
}
/**
* @brief start transfer in dma mode. 在dma 模式下开启数据传输
* @param hi2c: the handle points to the operation information.
这里他漏了一个注释参数,就是传输通道
* @param address: slave address. 从机地址
* @param start_stop: config gen start condition mode. 选择以下三个参数
* parameter as following values:
* - I2C_WITHOUT_START: transfer data without start condition.
* - I2C_GEN_START_READ: read data and generate start.
* - I2C_GEN_START_WRITE: send data and generate start.
* @retval i2c status.返回状态
*/
void i2c_start_transfer_dma(i2c_handle_type* hi2c, dma_channel_type* dma_channelx, uint16_t address, i2c_start_stop_mode_type start_stop)
{
if (hi2c->pcount > MAX_TRANSFER_CNT)
{
hi2c->psize = MAX_TRANSFER_CNT;
/* config dma */
i2c_dma_config(hi2c, dma_channelx, hi2c->pbuff, hi2c->psize);
i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_RELOAD_MODE, start_stop);
}
else
{
hi2c->psize = hi2c->pcount;
/* config dma */
i2c_dma_config(hi2c, dma_channelx, hi2c->pbuff, hi2c->psize);
i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_AUTO_STOP_MODE, start_stop);
}
}
/**
* @brief the master transmits data through polling mode. 主机通过轮询模式传输数据。
* @param hi2c: the handle points to the operation information. 外设结构体
* @param address: slave address. 从机地址
* @param pdata: data buffer. 数据缓冲区
* @param size: data size. 数据长度
* @param timeout: maximum waiting time. 最长等待时间
* @retval i2c status. 返回状态
*/
i2c_status_type i2c_master_transmit(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)
{
/* initialization parameters */
hi2c->pbuff = pdata;
hi2c->pcount = size;
hi2c->error_code = I2C_OK;
/* wait for the busy falg to be reset 等总线由忙转为可发送状态*/
if (i2c_wait_flag(hi2c, I2C_BUSYF_FLAG, SET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
{
return I2C_ERR_STEP_1; //如果超时了,返回状态1
}
/* start transfer 开始传输 */
i2c_start_transfer(hi2c, address, I2C_GEN_START_WRITE);
while (hi2c->pcount > 0)
{
/* wait for the tdis falg to be set 等待数据可传输 */
if(i2c_wait_flag(hi2c, I2C_TDIS_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
{
return I2C_ERR_STEP_2; //如果超时返回状态2
}
/* send data */
i2c_data_send(hi2c->i2cx, *hi2c->pbuff++);
hi2c->psize--;
hi2c->pcount--;
if ((hi2c->psize == 0) && (hi2c->pcount != 0))
{
/* wait for the tcrld falg to be set 等待从机返回状态 */
if (i2c_wait_flag(hi2c, I2C_TCRLD_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
{
return I2C_ERR_STEP_3; 返回错误状态3
}
/* continue transfer 由于还要传输数据 不等待START状态 */
i2c_start_transfer(hi2c, address, I2C_WITHOUT_START);
}
}
/* wait for the stop falg to be set 数据传输结束 等待STOP信号 */
if(i2c_wait_flag(hi2c, I2C_STOPF_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
{
return I2C_ERR_STEP_4; 返回错误状态 未等到STOP信号
}
/* clear stop flag 清除STOP状态 */
i2c_flag_clear(hi2c->i2cx, I2C_STOPF_FLAG);
/* reset ctrl2 register 重置外设寄存器 */
i2c_reset_ctrl2_register(hi2c);
return I2C_OK;
}
/**
* @brief the master receive data through polling mode. 主机通过轮询模式接收数据
* @param hi2c: the handle points to the operation information.
* @param address: slave address.
* @param pdata: data buffer.
* @param size: data size.
* @param timeout: maximum waiting time.
* @retval i2c status.
*/
i2c_status_type i2c_master_receive(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)
{
/* initialization parameters */
hi2c->pbuff = pdata;
hi2c->pcount = size;
hi2c->error_code = I2C_OK;
/* wait for the busy falg to be reset 等待总结的空闲*/
if (i2c_wait_flag(hi2c, I2C_BUSYF_FLAG, SET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
{
return I2C_ERR_STEP_1;
}
/* start transfer 开始传输*/
i2c_start_transfer(hi2c, address, I2C_GEN_START_READ);
while (hi2c->pcount > 0)
{
/* wait for the rdbf falg to be set 等 I2C_STS_RDBF 寄存器为1 */
if(i2c_wait_flag(hi2c, I2C_RDBF_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
{
return I2C_ERR_STEP_2;
}
/* read data */
(*hi2c->pbuff++) = i2c_data_receive(hi2c->i2cx);
hi2c->pcount--;
hi2c->psize--;
if ((hi2c->psize == 0) && (hi2c->pcount != 0))
{
/* wait for the tcrld falg to be set 等待 I2C_STS_TCRLD 寄存器为1 */
if (i2c_wait_flag(hi2c, I2C_TCRLD_FLAG, RESET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
{
return I2C_ERR_STEP_3;
}
/* continue transfer 继续接收 不待待START信号 */
i2c_start_transfer(hi2c, address, I2C_WITHOUT_START);
}
}
抄了好久的作业,读到这里基本上用的函数也差不多了。我怕抄太多了把网站的空间给占完了。
【楼下将我的I2C驱动AH10】展示。
|