Linux-Ubuntu之I2C通信
- 一,I2C通信原理
- 1.写时序
- 2.读时序
- 二,代码实现
- 三,显示
一,I2C通信原理
使用I2C接口驱动AP3216C传感器,该传感器能实现两个效果,一个是感应光强,另一个是探测物体与传感器的接近程度,这个实验就是从这个传感器对应的寄存器中,读取数据。I2C通信使用两根两条线(SDA和SCL)在主机和设备之间实现通信,这个实验做完感觉和51的还是有点区别的,51的注重具体的时序来控制数据传输,要自己去控制SCL的高低电平和上升下降沿,从内部电平信号来实现,这个linux实验主要是控制寄存器的状态标准位来控制数据的传输,不用控制时序,可能是51比较简单,控制电平信号也相对容易实现,这个芯片就更智能一些,控制寄存器,好像是自动就控制了电平信号。
1.写时序
开始信号→发送设备的地址,说明进行写操作→设备发送ACK应答信号→再发送开始信号→发送要写入数据寄存器的地址→设备发送ACK应答信号→发送要写入寄存器的数据→设备发送应答信号→主机发送停止信号
在程序上,主要分成了三个部分:1-4:开始信号,写设备地址并说明传输方向,5-7:写寄存器地址,8-10:对设备的寄存器进行写入,做STOP信号。
2.读时序
读操作和写操作,前面两个环节是一样的,不一样在于后面两个部分,读操作要再次写入设备的地址,最后对设备的寄存器数据进行读取。
开始信号→发送设备的地址,说明进行读写操作,虽然整体是读操作,但是这两步要进行还是写,因此在七位设备地址后面跟的是写标志位→设备发送ACK应答信号→再发送开始信号→发送要读取数据寄存器的地址→设备发送ACK应答信号→重新发送start信号→发送设备地址+读标志位→设备发送ACK应答信号→主机从设备的寄存器中读取数据→主机发送NO ACK信号,表示读取完成→主机发送STOP停止信号。
在程序上,分为四个部分:1-4:开始信号,写设备地址并说明传输方向为写,5-7:写寄存器地址,8-11:再次start,写设备地址并且说明是读,12-14:进行读操作,并且做NOACK和STOP信号。
二,代码实现
I2C部分代码,实现的是整个读和写时序进行的操作,利用i2c_master_transfer(I2C_Type *base,struct i2c_transfer_all *xfer);/*最终的处理函数*/
这个函数,将start函数,stop函数,错误检查函数,读写函数放到一起,真正实现操作的是在传感器函数中,要对读写进行说明,怎么样去用这个处理函数。
/i2c.h/
#ifndef _DSP_RTC_H
#define _DSP_RTC_H
#include "imx6ul.h"
/*时间宏定义*/
#define SECONDS_IN_A_DAY 86400
#define SECONDS_IN_A_HOUR 3600
#define SECONDS_IN_A_MINUTE 60
#define DAYS_IN_A_YEAR 365
#define YEAR_RANGE_START 1970
#define YEAR_RANGE_END 2099
/*时间结构体*/
struct rtc_datetime{
unsigned short year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char second;
};
/*rtc初始化*/
void rtc_init(void);
/*使能*/
void rtc_enable(void);
/*关闭使能*/
void rtc_disable(void);
/*判断润年*/
unsigned char rtc_isleapyear(unsigned short year);
/*将年月日时间转换为秒函数*/
unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime);
/*将相应秒数,写入到相应寄存器中*/
void rtc_in_register(struct rtc_datetime *rtctime);
/*将读出第秒数,转化为真的时间*/
void rtc_convertseconds_to_datetime(u64 seconds, struct rtc_datetime *datetime);
/*读寄存器值,从而得到时间*/
void rtc_out_register(struct rtc_datetime *rtctime);
#endif
/i2c.c/
#include "dsp_rtc.h"
/*rtc初始化*/
void rtc_init(void)
{
SNVS->HPCOMR |=(1<<31)|(1<<8);
struct rtc_datetime rtcDate;
rtcDate.year=2025;
rtcDate.month=1;
rtcDate.day=4;
rtcDate.hour=8;
rtcDate.minute=0;
rtcDate.second=0;
rtc_in_register(&rtcDate);
rtc_enable();//使能
}
/*使能*/
void rtc_enable(void)
{
SNVS->LPCR |=1<<0;
while((SNVS->LPCR & 0x01)==0);//当为0时候,一直在while里面循环
}
/*关闭使能*/
void rtc_disable(void)
{
SNVS->LPCR &=~(1<<0);
while((SNVS->LPCR & 0x01)==1);//为1的时候,一直循环
}
/*判断润年*/
unsigned char rtc_isleapyear(unsigned short year)
{
unsigned char value=0;
if(year % 400 == 0)
value = 1;
else
{
if((year % 4 == 0) && (year % 100 != 0))
value = 1;
else
value = 0;
}
return value;
}
/*将年月日时间转换为秒函数*/
unsigned int rtc_coverdate_to_seconds(struct rtc_datetime *datetime)
{
unsigned short i = 0;
unsigned int seconds = 0;
unsigned int days = 0;
unsigned short monthdays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U};
for(i = 1970; i < datetime->year; i++)
{
days += DAYS_IN_A_YEAR; /* 平年,每年365天 */
if(rtc_isleapyear(i)) days += 1;/* 闰年多加一天 */
}
days += monthdays[datetime->month];
if(rtc_isleapyear(i) && (datetime->month >= 3)) days += 1;/* 闰年,并且当前月份大于等于3月的话加一天 */
days += datetime->day - 1;
seconds = days * SECONDS_IN_A_DAY +
datetime->hour * SECONDS_IN_A_HOUR +
datetime->minute * SECONDS_IN_A_MINUTE +
datetime->second;
return seconds;
}
/*将相应秒数,写入到相应寄存器中*/
void rtc_in_register(struct rtc_datetime *rtctime)
{
unsigned int seconds = 0;
rtc_disable();//关闭使能
seconds=rtc_coverdate_to_seconds(rtctime);
/*放入寄存器中*/
SNVS->LPSRTCMR = (unsigned int)(seconds>>17);
SNVS->LPSRTCLR = (unsigned int)(seconds<<15);
}
/*将读出第秒数,转化为真的时间*/
void rtc_convertseconds_to_datetime(u64 seconds, struct rtc_datetime *datetime)
{
u64 x;
u64 secondsRemaining, days;
unsigned short daysInYear;
/* 每个月的天数 */
unsigned char daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
secondsRemaining = seconds; /* 剩余秒数初始化 */
days = secondsRemaining / SECONDS_IN_A_DAY + 1; /* 根据秒数计算天数,加1是当前天数 */
secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY; /*计算天数以后剩余的秒数 */
/* 计算时、分、秒 */
datetime->hour = secondsRemaining / SECONDS_IN_A_HOUR;
secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR;
datetime->minute = secondsRemaining / 60;
datetime->second = secondsRemaining % SECONDS_IN_A_MINUTE;
/* 计算年 */
daysInYear = DAYS_IN_A_YEAR;
datetime->year = YEAR_RANGE_START;
while(days > daysInYear)
{
/* 根据天数计算年 */
days -= daysInYear;
datetime->year++;
/* 处理闰年 */
if (!rtc_isleapyear(datetime->year))
daysInYear = DAYS_IN_A_YEAR;
else /*闰年,天数加一 */
daysInYear = DAYS_IN_A_YEAR + 1;
}
/*根据剩余的天数计算月份 */
if(rtc_isleapyear(datetime->year)) /* 如果是闰年的话2月加一天 */
daysPerMonth[2] = 29;
for(x = 1; x <= 12; x++)
{
if (days <= daysPerMonth[x])
{
datetime->month = x;
break;
}
else
{
days -= daysPerMonth[x];
}
}
datetime->day = days;
}
/*读寄存器值,从而得到时间*/
void rtc_out_register(struct rtc_datetime *rtctime)
{
uint64_t seconds = 0;
seconds = (uint64_t)((uint64_t)SNVS->LPSRTCMR<<17|SNVS->LPSRTCLR>>15);
rtc_convertseconds_to_datetime(seconds,rtctime);
}
AP3216C函数,在定义的结构体中,进行配置,说明设备地址,设备寄存器地址,读或者写操作,并将读或者写的数据放到定义的变量中。
/ap3216c.h/
#ifndef _DSP_AP3216C_H
#define _DSP_AP3216C_H
#include "imx6ul.h"
/*从机地址*/
#define AP3216C_ADDR 0X1E
/*寄存器地址*/
#define AP3216C_SYSTEMCONG 0X00
#define AP3216C_INTSTATUS 0X01
#define AP3216C_INTCLEAR 0X02
#define AP3216C_IRDATALOW 0X0A
#define AP3216C_IRDATAHIGH 0X0B
#define AP3216C_ALSDATALOW 0X0C
#define AP3216C_ALSDATAHIGH 0X0D
#define AP3216C_PSDATALOW 0X0E
#define AP3216C_PSDATAHIGH 0X0F
void ap3216c_init(void);
unsigned char ap3216c_write_one_byte(unsigned char addr,unsigned char reg,unsigned char data);
unsigned char ap3216c_read_one_byte(unsigned char addr,unsigned char reg);
void ap3216c_redata(unsigned short *ir,unsigned short *ps,unsigned short *als);
#endif
/ap3216c.c/
#include "dsp_ap3216c.h"
#include "dsp_i2c.h"
#include "dsp_gpio.h"
#include "dsp_delay.h"
#include "stdio.h"
void ap3216c_init(void)
{
unsigned char result=0;
/*1.引脚设置*/
IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL,1);
IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL,0X70b0);
IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA,1);
IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA,0X70b0);
/*2.I2C接口初始化*/
i2c_init(I2C1);
/*3.传感器初始化*/
ap3216c_write_one_byte(AP3216C_ADDR,AP3216C_SYSTEMCONG,0x04);//初始化
delay(50);
ap3216c_write_one_byte(AP3216C_ADDR,AP3216C_SYSTEMCONG,0x03);
result=ap3216c_read_one_byte(AP3216C_ADDR,AP3216C_SYSTEMCONG);
printf("AP3216C_SYSTEMCONG的result= %d \r\n",result);
}
/*写一个字节设置*/
unsigned char ap3216c_write_one_byte(unsigned char addr,unsigned char reg,unsigned char data)
{
unsigned char writedata = data;
unsigned char status =0;
struct i2c_transfer_all masterXfer;
masterXfer.slave_address=addr;//从机地址
masterXfer.transfer_direction=KI2c_Write;//设置为写模式
masterXfer.register_address =reg;//寄存器地址
masterXfer.register_address_size = 1;
masterXfer.data = &writedata;
masterXfer.data_size = 1;
if(i2c_master_transfer(I2C1,&masterXfer))//0正常 其他不正常
{
status = 1;
}
return status;
}
/*读一个字节*/
unsigned char ap3216c_read_one_byte(unsigned char addr,unsigned char reg)
{
unsigned char readdata = 0;
struct i2c_transfer_all masterXfer;
masterXfer.slave_address=addr;//从机地址
masterXfer.transfer_direction=KI2c_read;//设置为写模式
masterXfer.register_address =reg;//寄存器地址
masterXfer.register_address_size = 1;
masterXfer.data = &readdata;
masterXfer.data_size = 1;
i2c_master_transfer(I2C1,&masterXfer);//读操作
return readdata;
}
/*读传感器数据*/
void ap3216c_redata(unsigned short *ir,unsigned short *ps,unsigned short *als)
{
unsigned char buf[6]={};
unsigned char i = 0;
for(i=0;i<6;i++)//将寄存器数据放到buf中
{
buf[i]=ap3216c_read_one_byte(AP3216C_ADDR,AP3216C_IRDATALOW+i);
}
if(buf[0]&0x80)//这个标志判断ir als是否有效
{
*ir=0;
*ps=0;
}else
{
*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
*als = ((unsigned short)buf[3] << 8) | buf[2];
}
if(buf[4]&0x40)//判断ps是否有效
{
*ps = 0;
}else
{
*ps = ((unsigned short)(buf[5] & 0x3f)<<4 | (buf[4] & 0x0f));
}
}
主函数:
#include "main.h"
#include "dsp_clk.h"
#include "dsp_led.h"
#include "dsp_delay.h"
#include "beep.h"
#include "dsp_key.h"
#include "dsp_int.h"
#include "dsp_exti.h"
#include "dsp_epit.h"
#include "dsp_uart.h"
#include "stdio.h"
#include "dsp_lcd.h"
#include "dsp_lcdapi.h"
#include "dsp_rtc.h"
#include "dsp_i2c.h"
#include "dsp_ap3216c.h"
int main(void)
{
// int b=0;
unsigned short ir,ps,als;
unsigned char kkkk=0;
struct rtc_datetime rtc_now_time;
char buf[160]={};
int_init();//中断初始化
imx6u_clkinit();//时钟初始化
key_init();//按键初始化
clk_enable();//时钟初始化
uart_init();//串口初始化
beep_init();//凤鸣器初始化
led_init();//led初始化
lcd_init();//LCD读ID号
rtc_init();//RTC初始化
ap3216c_init();//传感器初始化
tftlcd_dev.forecolor = LCD_RED;
tftlcd_dev.backcolor = LCD_WHITE;
// unsigned char *b,*c,*f;
// unsigned short *a;
// lcd_show_string(10,40,260,32,32,(char *)"Fucking high");
lcd_show_string(100, 160, 200, 32, 32, (char*)" IR:");
lcd_show_string(100, 200, 200, 32, 32, (char*)" PS:");
lcd_show_string(100, 240, 200, 32, 32, (char*)"ALS:");
while(1)
{
ap3216c_redata(&ir,&ps,&als);
lcd_shownum(200, 160, ir, 5, 32);
lcd_shownum(200, 200, ps, 5, 32);
lcd_shownum(200, 240, als, 5, 32);
printf("ir = %d ps = %d als = %d \r\n",ir,ps,als);
tftlcd_dev.forecolor = LCD_RED;
rtc_out_register(&rtc_now_time);
sprintf(buf,"%d.%d.%d-%d:%d:%d\r\n",rtc_now_time.year,rtc_now_time.month,
rtc_now_time.day,rtc_now_time.hour,rtc_now_time.minute,rtc_now_time.second);
lcd_show_string(70,70,240,32,32,(char *)buf);
led_mode(kkkk);
delay(1000);
kkkk = !kkkk;
}
return 0;
}