硬件驱动——I.MX6ULL裸机启动(6)(i2c相关设置)
※重点:
1
i2c:i2c是一种双向的二线制同步串行总线协议,由飞利浦公司于1980年代开发,主要用于微控制器与外围设备之间的短距离,低速率通信,其核心特点是仅需两根信号线(SCL和SDA)即可实现多设备连接,支持主从架构和半双工通信
简述i2c发送一个字节的时序:先将SDA线置于低电平状态发送一个起始信号,然后将设备地址由高位到低位进行发送给从机,最后一位比特置于0,从机发送一个应答信号ACK,主机向从机发送一个寄存器地址,从机发送应答信号ACK,主机向从机发送一个数据,从机发送一个应答信号ACK,主机发送一个停止信号。
2
简述通过i2c写入某个设备的时序:先将SDA线置于低电平状态发送一个起始信号,然后将设备地址由高位到低位进行发送给从机,最后一个比特位置于0,从机发送一个应答信号ACK,主机向从机发送一个寄存器地址,从机发送一个应答信号ACK,主机向从机依次发送1个字节的数据,每发送一个数据,从机发送一个应答信号ACK,直到发完数据为止,主机发送一个停止信号。
3
简述通过i2c读取某个设备的时序:先将SDA数据线置于低电平状态发送一个起始信号,然后将设备地址由高到低进行发送给从机,最后一个比特置于0位,从机发送应答信号ACK,主机向从机发送一个寄存器地址,从机发送应答信号ACK,主机再次发送一个起始信号,再将设备地址发送给从机,最后一个比特位置于1,从机发送应答信号ACK,从机向主机每发送一个字节数据,主机发送应答信号ACK,发送到最后一个数据结束,主机发送信号NO ACK,主机发送一个停止信号。
4.
i2c在硬件设计时需要注意的问题:在SDA和SCL两条线上必须装两个上拉电阻(4.7~10kΩ),上拉电阻越小越强
一.i2c相关定义和概念
i2c通信主机时芯片与芯片之间,为半双工的通信方式
i2c连接方式为两根线SDA(数据线),SCL(时钟线),时钟线的作用是统一收发速率的
电路设置应注意的问题:在SDA和SCL两条线上必须装两个上拉电阻(4.7~10kΩ),上拉电阻越小越强
上拉电阻的作用是:保证SCL和SDA都在释放总线时,让SDA保持高电平
二.i2c的时序问题
空闲时:SCL和SDA都保持高电平状态
发数据时从左到右(MSB先行原则),SCL在高电平采样
接收方发送应答信号,每发完一个字节都要进行一次应答(答的低电平为ACK,高电平为NACK(默认))
停止信号:SCL为高电平时,SCL会将SDA拉成高电平然后处于空闲状态
i2c的发送频率由主机决定
i2c协议:按七个比特位来计算,第八个比特为0,表示主机发往从机,为1是表示从机发送给主机
第八个比特位为R/W数据流向位
三.AT24C08
大小为2k,256*8,有256个字节
波特率为:100khz~400khz
四.相关代码
#include "i2c.h"#include "fsl_iomuxc.h"
#include "delay.h"#define IEN (7)
#define MSTA (5)
#define MTX (4)
#define TXAK (3)
#define RSTA (2)#define ICF (7)
#define IBB (5)
#define IAL (4)
#define IIF (1)void init_i2c1(void)
{IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL,1); //配置SCL时钟总线的IO复用功能IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA,1); //配置SDA数据总线的IO复用功能IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL,0xF0B0); //配置SCL时钟总线的电气属性IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA,0xF0B0); //配置SDA数据总线的电气属性I2C1->I2CR &= ~(1 << 7); //在设置波特率之前将该位清零I2C1->IFDR = 0x15; //设置i2c的波特率I2C1->I2CR |= (1 << 7); //在设置完波特率后将该位置1}
void i2c_write(I2C_Type *base, unsigned char device_address, unsigned short reg_address, int reg_len, const unsigned char *data, int len)
{base->I2SR &= ~((1 << IAL) | (1 << IIF)); //在启动通信之前需要将TAL和IIF位清零while((base->I2SR & (1 << ICF)) == 0); //阻塞等待,判断数据是否在传输,当ICF位为1则传输完成base->I2CR |= (1 << MSTA) | (1 << MTX); //将MSTA位置1,之后会发送一个起始位。发送从机地址时,MTX位总是为1base->I2CR &= ~(1 << TXAK); //在发送数据时表面是否监测应答,接收数据时表面是否回应应答,将TXAK位清零则开启应答base->I2SR &= ~(1 << IIF); //无论发送数据还是接收数据之前,都必须将IIF为清零base->I2DR = device_address << 1; //将设备地址向左移一个比特,将最后一位比特置0while((base->I2SR & (1 << IIF)) == 0); //若IIF位置1则发送完毕int i;for(i = 0; i < reg_len; ++i){base->I2SR &= ~(1 << IIF); //将IIF位清零base->I2DR = reg_address >> (reg_len - i - 1) * 8; //发送寄存器的地址while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1}while(len--) //发送的字节数--{base->I2SR &= ~(1 << IIF); //将IIF位清零base->I2DR = *data++; //将数据以一个字节为单位,依次进行发送while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1}base->I2CR &= ~(1 << MSTA); //将MSTA位清零,主机会发送一个停止信号while((base->I2SR & (1 << IBB)) != 0) //判断IBB位是否为0,为0则为已发送停止信号{delayus(100); //延时一毫秒}
}void i2c_read(I2C_Type *base, unsigned char device_address, unsigned short reg_address, int reg_len, unsigned char *data, int len)
{base->I2SR &= ~((1 << IAL) | (1 << IIF)); //在启动通信之前需要将TAL和IIF位清零while((base->I2SR & (1 << ICF)) == 0); //阻塞等待,判断数据是否在传输,当ICF位为1则传输完成base->I2CR |= (1 << MSTA) | (1 << MTX); //将MSTA位置1,之后会发送一个起始位。发送从机地址时,MTX位总是为1base->I2CR &= ~(1 << TXAK); //在发送数据时表面是否监测应答,接收数据时表面是否回应应答,将TXAK位清零则开启应答base->I2SR &= ~(1 << IIF); //将IIF位清零base->I2DR = device_address << 1; //发送寄存器的地址while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1int i; //0x1234 reg_len = 2for(i = 0; i < reg_len; ++i){base->I2SR &= ~(1 << IIF); //将IIF位清零base->I2DR = reg_address >> (reg_len - i - 1) * 8; //发送寄存器的地址while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1}base->I2CR |= (1 << RSTA); //当RSTA位为1时,主机重新发起起始信号base->I2SR &= ~(1 << IIF); //将IIF位清零base->I2DR = device_address << 1 | 1; //发送寄存器的地址while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1base->I2CR &= ~(1 << MTX); //发送从机地址时,MTX位总是为1,接收数据时MTX位要为0base->I2SR &= ~(1 << IIF); //将IIF位清零if(1 == len){base->I2CR |= (1 << TXAK); //在发送数据时表面是否监测应答,接收数据时表面是否回应应答,将TXAK位清零则开启应答}*data = base->I2DR; //在数据传输之前需要假读一次while(len-- != 0){while((base->I2SR & (1 << IIF)) == 0); //判断IIF位是否为1base->I2SR &= ~(1 << IIF); //将IIF位清零if(len == 0){base->I2CR &= ~((1 << MSTA) | (1 << TXAK)); //MSTA发送一个停止位while((base->I2SR & (1 << IBB)) != 0) //判断IBB位是否为0,为0则为已发送停止信号{delayus(100); //延时一毫秒}}else if(len == 1){base->I2CR |= (1 << TXAK); //在发送数据时表面是否监测应答,接收数据时表面是否回应应答,将TXAK位清零则开启应答,置1则发送NO ACK}*data++ = base->I2DR;}
}void xfer(I2C_Type *base, struct I2C_MSG *msg)
{if(msg->driection == I2C_Write){i2c_write(base, msg->dev_address, msg->reg_address, msg->reg_len, msg->data, msg->len);}else{i2c_read(base, msg->dev_address, msg->reg_address, msg->reg_len, msg->data, msg->len);}}
#ifndef __I2C_H__
#define __I2C_H__
#include "MCIMX6Y2.h"
extern void init_i2c1(void);
extern void i2c_write(I2C_Type *base, unsigned char device_address, unsigned short reg_address, int reg_len, const unsigned char *data, int len);
extern void i2c_read(I2C_Type *base, unsigned char device_address, unsigned short reg_address, int reg_len, unsigned char *data, int len);
enum I2C_Driection
{I2C_Write = 0,I2C_Read = 1
};struct I2C_MSG
{unsigned char dev_address;unsigned short reg_address;int reg_len;unsigned char *data;int len;enum I2C_Driection driection;
};extern void xfer(I2C_Type *base, struct I2C_MSG *msg);
#endif
#include "i2c.h"
#include "MCIMX6Y2.h"float lm75_get_temperature(void)
{unsigned char buffer[2] = {0};short s;struct I2C_MSG msg ={.driection = I2C_Read,.dev_address = 0x48,.reg_address = 0,.reg_len = 1,.data = buffer,.len = 2};xfer(I2C1, &msg);//i2c_read(I2C1,0x48,0,1,buffer,2);s = buffer[0] << 8;s |= buffer[1];s >>= 7;return s * 0.5;}
#include "beep.h"
#include "led.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"
#include "gpt.h"
#include "delay.h"
#include "uart.h"
#include "stdio.h"
#include "i2c.h"
#include "string.h"
#include "lm75.h"int main(void)
{init_clock();system_interrupt_init();init_beep();init_led();
// init_key();
// init_epit1();init_gpt1();init_uart1();init_i2c1();// const char *s = "Hello";// i2c_write(I2C1, 0x50, 0, (unsigned char *)s, 5);// printf("I2C Write OK");// delayms(500);// unsigned char buffer[16] = {0};// i2c_read(I2C1, 0x50, 0, buffer, 5);// puts((char *)buffer);while(1){// char s1[16];// scanf("%s",s1);// i2c_write(I2C1, 0x50, 0, (unsigned char *)s1, strlen(s1));// printf("ok\n");// delayms(500);// char s2[16] = {0};// i2c_read(I2C1, 0x50, 0, (unsigned char *)s2, strlen(s1));// puts((char *)s2);delayms(500);float f = lm75_get_temperature();int i = f * 10;int n = i / 10;int m = i % 10;printf("%d.%d",n,m);}return 0;
}