Linux-Ubuntu之I2C通信

Linux-Ubuntu之I2C通信

一,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函数,错误检查函数,读写函数放到一起,真正实现操作的是在传感器函数中,要对读写进行说明,怎么样去用这个处理函数。

c 复制代码
/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函数,在定义的结构体中,进行配置,说明设备地址,设备寄存器地址,读或者写操作,并将读或者写的数据放到定义的变量中。

c 复制代码
/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)); 
    }

}

主函数:

c 复制代码
#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;    

}

三,显示

相关推荐
果子⌂4 分钟前
PostgreSQL --数据库操作
linux·数据库·postgresql
墨城之左32 分钟前
Windows 上安装 devsidecar 后,使用 WSL ubuntu ssl 报错
windows·ubuntu·ssl
倔强的石头10643 分钟前
【Linux指南】文件系统基础操作与路径管理
linux·运维·服务器
中科三方2 小时前
如何通过DNS解析实现负载均衡?有哪些优势?
运维·负载均衡
安科瑞刘鸿鹏2 小时前
双碳时代,能源调度的难题正从“发电侧”转向“企业侧”
大数据·运维·物联网·安全·能源
小呆瓜历险记2 小时前
ubuntu 22.04搭建SOC开发环境
linux·运维·ubuntu
码农101号2 小时前
Linux中shell流程控制语句
linux·运维·服务器
ajassi20002 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
聪明小萝卜2 小时前
无法与IP建立连接,未能下载VSCode服务器
运维·服务器
JuiceFS2 小时前
深度解析 JuiceFS 权限管理:Linux 多种安全机制全兼容
运维·后端