此处不具体介绍I2C模块,不介绍I2C硬件的实现原理,不分析其通信协议,仅介绍与编程有关的寄存器和驱动构件的实现。 一、I2C总线上的信号类型 I2C总线在传送数据过程中共有四种类型信号,分别是开始信号、停止信号、重新开始信号和应答信号。 二、主机向从机读/写1个字节数据的过程 1.主机向从机写1个字节数据的过程
首先主机产生START信号,然后发送一个7位从机地址,第8位是数据方向位(R/W),0表示主机发送数据,这时候主机等待从机的应答信号(ACK),收到应答信号,发送要访问的地址,等待从机的响应信号,收到响应信号时,发送1个字节的数据,等待从机的响应信号,当收到响应信号时,产生停止信号,结束发送信号。
2.主机从从机读1个字节数据的过程
首先主机产生START信号,然后发送一个从机地址,此时该地址的第8位为0,表明是向从机写命令,主机等待从机的应答信号(ACK),收到应答信号时,发送要访问的地址,继续等待从机的应答信号,当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送),所以主机发送重新开始信号,然后发送一个从机地址,此时该地址的第8位为1,表明将主机设置成接收模式(读),这时主机等待从机的应答信号,当收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,不再接收数据,主机进而产生停止信号,结束传送过程。
三、I2C寄存器 I2C的两个模块,所有的终端用户可以访问的I2C寄存器共有22个,每个模块各有11个8位寄存器,但每个模块常用的只有5个,这些寄存器复位为0。下表给出了I2C的模块0和模块1常用的10个寄存器,每个寄存器均可“读/写”。
1.I2C地址寄存器1(I2Cx_A1)
D7~D1(AD[7:1])当作为从机被寻址时,此字段包含I2C模块所使用的初始从机地址。此字段用于7位地址方案和10位地址方案中的低7位。
D0(0)—保留位。这个只读字段是保留的,读取值为0。
2.I2C分频寄存器(I2Cx_F)
D7~D6(MULT)—定义倍频因子MUL。此因子与SCL分频一起使用,以产生I2C波特率。
D5~D0(ICR)—时钟频率。此字段和MULT字段确定I2C波特率、SDA保持时间、SCL开始保持时间和SCL停止保持时间。
3.I2C控制寄存器1(I2Cx_C1)
D7(ICEN)—I2C使能。使能I2C模块的操作。0 禁用, 1使能。
D6(IICEN I2C)—I2C中断使能。使能I2C中断请求。0 禁用,1使能。
D5(MST)—主机模式选择。
D4(TX)—传送模式选择。
D3(TXAK)—传送应答使能。
D2(RSTA)—重复开始。
D1(WUEN)—唤醒使能。
D0(DMAEN)—DMA使能。
4.I2C状态寄存器(I2Cx_S)
D7(TCF)—传输完成标志。该位在一个字节和应答信号传输完成时被置位。
D6(IAAS)—作为从机被寻址。
D5(BUSY)—总线忙。
D4(ARBL)—仲裁丢失。
D3(RAM)—扩展地址匹配。
D2(SRW)—从机读/写。
D1(IICIF)—中断标志。
D0(RXAK)—接收应答。
5.I2C数据I / O寄存器(I2Cx_D)
在主机传送模式,当数据被写入到这个寄存器后,数据传输开始。首先发送最高位。在主机接收模式下,读取该寄存器开始接收下一个字节的数据。在从机模式下,同样的功能都可以在地址匹配后发生后获得。在主模式和从模式下的传输开始时,C1 [TX]位必须正确地反映所需的传输方向。
四、I2C驱动构件封装
<span style="font-size:12px;">//选择为发送模式void i2c_set_tx_mode(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BSET(I2C_C1_TX_SHIFT,I2C0_C1); //置位i2c0 TXAK } else { BSET(I2C_C1_TX_SHIFT,I2C1_C1); //置位i2c0 TXAK }}</span>
//选择为接受模式void i2c_set_rx_mode(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BCLR(I2C_C1_TX_SHIFT,I2C0_C1); //置位i2c0 TXAK } else { BCLR(I2C_C1_TX_SHIFT,I2C1_C1); //置位i2c0 TXAK }}//设置为从机模式void i2c_set_slave_mode(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BCLR(I2C_C1_MST_SHIFT,I2C0_C1); //清i2c0 MST } else { BCLR(I2C_C1_MST_SHIFT,I2C1_C1); //清i2c0 MST }}//设置为主机模式void i2c_set_master_mode(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BSET(I2C_C1_MST_SHIFT,I2C0_C1); //置位i2c0 MST } else { BSET(I2C_C1_MST_SHIFT,I2C1_C1); //置位i2c0 MST }}//发送非应答信号void i2c_give_nack(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BSET(I2C_C1_TXAK_SHIFT,I2C0_C1); //置位i2c0 TXAK } else { BSET(I2C_C1_TXAK_SHIFT,I2C1_C1); //置位i2c0 TXAK }}//发送应答信号void i2c_give_ack(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BCLR(I2C_C1_TXAK_SHIFT,I2C0_C1); //清i2c0 TXAK } else { BCLR(I2C_C1_TXAK_SHIFT,I2C1_C1); //清i2c0 TXAK }}//重新开始信号void i2c_repeated_start(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { BSET(I2C_C1_RSTA_SHIFT,I2C0_C1); //置位i2c0 RSTA } else { BSET(I2C_C1_RSTA_SHIFT,I2C1_C1); //置位i2c0 RSTA }}//写void i2c_write_byte(uint_8 I2C_No, uint_8 Data){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { I2C0_D = Data; } else { I2C1_D = Data; }}//读uint_8 i2c_read_byte(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { return I2C0_D; } else { return I2C1_D; }}//开始信号void i2c_start(uint_8 I2C_No){ i2c_set_master_mode(I2C_No); i2c_set_tx_mode(I2C_No);}//停止信号void i2c_stop(uint_8 I2C_No){ i2c_set_slave_mode(I2C_No); i2c_set_rx_mode(I2C_No);}//等待void i2c_wait(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { // wait flag while(BGET(I2C_S_IICIF_SHIFT,I2C0_S) == 0); //写1清零 BSET(I2C_S_IICIF_SHIFT,I2C0_S); } else { // wait flag while(BGET(I2C_S_IICIF_SHIFT,I2C1_S) == 0); //写1清零 BSET(I2C_S_IICIF_SHIFT,I2C1_S); }}<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"> </span>//获取应答信号uint_16 i2c_get_ack(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { if(BGET(I2C_S_RXAK_SHIFT,I2C0_S) == 0) return TRUE; else return FALSE; } else { if(BGET(I2C_S_RXAK_SHIFT,I2C1_S) == 0) return TRUE; else return FALSE; }}void hal_i2c_init(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } //获取I2C模块的基址 I2C_MemMapPtr num = hal_i2c_get_base_address(I2C_No); if(I2C0 == num) //Acelerometro { SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK; SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTE_MASK; #ifdef FRDM_REVA PORTC_PCR8 = PORT_PCR_MUX(2); PORTC_PCR9 = PORT_PCR_MUX(2); #else //Port E multiplexing control,配置引脚复用为I2C0功能 //I2C0 SCL使用PTE24,I2C0 SDA使用PTE25 PORTE_PCR24 = PORT_PCR_MUX(5); //I2C0 SCL PORTE_PCR25 = PORT_PCR_MUX(5); //I2C0 SDA #endif //设置MULT和ICR,KL25的MCU总线频率为24MHz,在总线上分频得75kHz I2C0_F = 0x14; //波特率为300k BSET(I2C_C1_IICEN_SHIFT,I2C0_C1);//开i2c0模块使能 } else //Magnetometro { SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK; SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK; // configure GPIO for I2C function //PORTE_PCR24 = PORT_PCR_MUX(5); //PORTE_PCR25 = PORT_PCR_MUX(5); PORTC_PCR10 = PORT_PCR_MUX(2); PORTC_PCR11 = PORT_PCR_MUX(2); I2C0_F = 0x14; //波特率为300k BSET(I2C_C1_IICEN_SHIFT,I2C0_C1);//开i2c0模块使能 }}void hal_i2c_deinit(uint_8 I2C_No){ if(I2C_No<0||I2C_No>1) //如果模块号写错则强制其为0 { I2C_No = 0; } if(0 == I2C_No) { I2C0_C1 = 0x00; SIM_SCGC4 &= ~SIM_SCGC4_I2C0_MASK; } else { I2C1_C1 = 0x00; SIM_SCGC4 &= ~SIM_SCGC4_I2C1_MASK; }}I2C_MemMapPtr hal_i2c_get_base_address(uint_8 I2C_No){ switch(I2C_No) { case 0: return I2C0_BASE_PTR; case 1: return I2C1_BASE_PTR; break; }} |