本帖最后由 Afanx 于 2022-7-19 18:02 编辑
二、I2C做主机,轮询读写
1、I2C主机轮询发送
EV5:STARTBF=1,起始位产生。 EV6:ADDRF=1,从机响应地址。 EV8_1/EV8:TXDATE=1,发送寄存器为空,写DAT寄存器。 EV8_2:TXDATE =1,BSF=1,请求设置停止位。
主机发送步骤:
(1)保证I2C不在使用中,等待BUSY=0; (2)发送START信号,等待EV5 (STARTBF=1); (3)写从机地址,等待EV6 (ADDRF=1); (4)发送数据,发送之前判断TXDATE标志,保证DAT为空开始写入数据; (5)等待最后一个字节位发送完成; (6)发送STOP信号;
注意在发送从机地址后写DAT后,数据会立即发送至移位缓冲器中,所以此时TXDATE不会清0(EV8_1),可以继续写下一个数据。在程序中只需要关注在TXDATE置1时写DAT即可。 写完第N个数据后,停止写数据,此时移位寄存器正在发送第N-1个数据。等移位寄存器发送第N个数据时可以不关心TXDATE置1,发送完成后DAT寄存器和移位寄存器同时为空时,BSF置1。此时发送STOP,等待BUSY清0,结束一次通信。 定义超时时间和错误状态: - #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
- #define I2CT_LONG_TIMEOUT ((uint32_t)(3 * I2CT_FLAG_TIMEOUT))
- typedef enum {
- MASTER_OK = 0,
- MASTER_BUSY,
- MASTER_MODE,
- MASTER_TXMODE,
- MASTER_RXMODE,
- MASTER_SENDING,
- MASTER_SENDED,
- MASTER_RECVD,
- MASTER_BYTEF,
- MASTER_BUSERR,
- MASTER_UNKNOW,
- SLAVE_OK = 20,
- SLAVE_BUSY,
- SLAVE_MODE,
- SLAVE_BUSERR,
- SLAVE_UNKNOW,
- } ErrCode_t;
主机轮询发送函数:
- int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
- {
- uint32_t I2CTimeout;
- if (regAddr < 0 && len == 0) return 0; /* 至少发送一个数据 */
- /* 1.保证I2C不在使用中 */
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_BUSY;
- }
- }
- /* 2.发送START信号 */
- I2C_GenerateStart(I2Cx, ENABLE);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) { // EV5
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_MODE;
- }
- }
- /* 3.写从机地址 */
- I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_SEND);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG)) { // EV6
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_TXMODE;
- }
- }
- /* 4.写寄存器地址 */
- if (regAddr >= 0) { /* 寄存器写 */
- I2C_SendData(I2Cx, (uint8_t)regAddr);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) { // EV8
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_SENDING;
- }
- }
- }
- /* 4.发送数据 */
- while (len--) {
- I2C_SendData(I2Cx, *datPtr++);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) { // EV8
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_SENDING;
- }
- }
- }
- /* 5.等待最后一个字节发送完成 */
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED)) { // EV8_2
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_SENDED;
- }
- }
- /* 6.发送STOP信号 */
- I2C_GenerateStop(I2Cx, ENABLE);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_BUSY;
- }
- }
- /* 7.发送完成 */
- return MASTER_OK;
- }
int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
参数说明: uint8_t slaveAddr:Bit[7:1]为从机地址,不关心最低位。 short regAddr:发送从器件寄存器地址(8bit),如果不需要发送,填-1 。【寄存器写】 uint8_t* datPtr:发送数据Buf的地址。
返回值:MASTER_OK表示通信正常,其他表示错误。
2、I2C主机轮询接收
EV5:STARTBF=1,起始位产生。 EV6:ADDRF=1,从机响应地址。 EV7/EV7_1: RXDATNE=1,读 DAT 寄存器。
主机接收步骤:
(1)保证I2C不在使用中,等待BUSY=0; (2)发送START信号,等待EV5 (STARTBF=1); (3)写从机地址,等待EV6 (ADDRF=1); (4)打开接收数据应答ACK,等待接收数据完成,判断RXDATNE标志读DAT,在接收最后一个字节完成前设置NACK和STOP信号。 (5)等待STOP发送,等待BUSY信号;
主机轮询接收函数: - int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
- {
- uint32_t I2CTimeout;
- int ret;
- if (len == 0) return 0; /* 至少读取一个数据 */
- if (regAddr >= 0) { /* 寄存器读 */
- ret = I2C_Write(I2Cx, slaveAddr, regAddr, datPtr, 0);
- if (ret != MASTER_OK) return ret;
- }
- /* 1.保证I2C不在使用中 */
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_BUSY;
- }
- }
- /* 2.发送START信号 */
- I2C_GenerateStart(I2Cx, ENABLE);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) { // EV5
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_MODE;
- }
- }
- /* 3.写从机地址 */
- I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_RECV);
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_RXMODE_FLAG)) { // EV6
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_TXMODE;
- }
- }
- /* 4.接收数据 */
- I2C_ConfigAck(I2Cx, ENABLE); /* 开启应答 */
- while (len--) {
- if (len == 0) { /* 接收最后一个数据前关闭应答,准备STOP */ // EV7_1
- I2C_ConfigAck(I2Cx, DISABLE);
- I2C_GenerateStop(I2Cx, ENABLE);
- }
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE)) { // EV7
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_BYTEF;
- }
- }
- *datPtr++ = I2C_RecvData(I2Cx);
- }
- /* 5.等待BUSY */
- I2CTimeout = I2CT_LONG_TIMEOUT;
- while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
- if ((I2CTimeout--) == 0) {
- I2C_ResetInit(I2Cx);
- return MASTER_BUSY;
- }
- }
- /* 6.接收完成 */
- return MASTER_OK;
- }
如果需要【寄存器读】,在读之前写一个数据。设置regAddr值>=0,否则直接读,设置regAddr为-1即可。
主机轮询读写函数声明: - int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
- int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
|