Linux-Ubuntu之I2C通信

news2025/1/7 5:51:47

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;    

}

三,显示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271420.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Trimble天宝X9三维扫描仪为建筑外墙检测提供了全新的解决方案【沪敖3D】

随着城市化进程的快速推进&#xff0c;城市高层建筑不断增多&#xff0c;对建筑质量的要求也在不断提高。建筑外墙检测&#xff0c;如平整度和垂直度检测&#xff0c;是衡量建筑质量的重要指标之一。传统人工检测方法不仅操作繁琐、效率低下&#xff0c;还难以全面反映墙体的真…

主机A与主机B建立TCP连接的三次握手过程

&#xff08; 1 &#xff09;主机 A 的 TCP 向主机 B 发出连接请求 SYN 报文段&#xff08;第一次握手&#xff09;。&#xff08; 1 分&#xff09; &#xff08; 2 &#xff09;一旦包含 SYN 报文段的 IP 数据报到达主机 B &#xff0c; SYN 报文段被从数据报…

【GUI-pyqt5】QObject类

1. QObject模块详解 1.1 描述 所有Qt对象的父类 1.2 功能和作用 1.2.1 对象名称和属性 1.2.1.1 API API功能备注 setObjectName("唯一名称") 给一个Qt对象设置一个名称 一般这个名称是唯一的&#xff0c;当做对象ID来使用 objectName() 获取一个对象名称 - set…

C++Primer 变量

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

VScode怎么重启

原文链接&#xff1a;【vscode】vscode重新启动 键盘按下 Ctrl Shift p 打开命令行&#xff0c;如下图&#xff1a; 输入Reload Window&#xff0c;如下图&#xff1a;

(leetcode算法题)382. 链表随机节点

如果给你一个 智能记录 k行内容的小笔记本&#xff0c;从一本你也不知道有多少行的 C Primer 中进行摘抄&#xff0c;你应该怎么做才能让抄写的时候能让书中的每一行都等概率的出现在小笔记本中&#xff1f; 答&#xff1a;准备好一个公平的轮盘和一个巨大的摇奖机&#xff0c…

MIPI_DPU 综合(DPU+MIPI+Demosaic+VDMA 通路)

目录 1. 简介 2. 创建 Platform 2.1 Block Design 2.1.1 DPU PFM Lite 2.1.2 DPU prj 2.1.3 DPU MIPI Platform 2.2 pin 约束 2.2.1 GPIO 约束 2.2.2 IIC 约束 2.1.3 DPHY 约束 3. 报错总结 3.1 AXI_M 必须顺序引用 3.2 DPU 地址分配错误 4. Design Example 4.…

亚信安全2025年第1期《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件51起&#xff0c;本周勒索事件数量降至近一年来最低&#xff0c;需注意防范。从整体上看Ransomhub依旧是影响最严重的勒索家族&#xff1b;本周Acrusmedia和Safepay也是两个活动频繁的恶意家族&#xff0c;需要注意防范。本周&#…

麒麟服务器安装kafka--亲测

我这安装的是单机版本的&#xff1a; 下载地址&#xff1a;Index of /kafka/3.9.0 我下载的是&#xff1a;https://dlcdn.apache.org/zookeeper/zookeeper-3.9.3/apache-zookeeper-3.9.3-bin.tar.gz https://dlcdn.apache.org/kafka/3.9.0/kafka_2.12-3.9.0.tgz 一、下载并上…

利用Deeplearning4j进行 图像识别

目录 图像识别简介 神经网络 感知器 前馈神经网络 自动编码器 受限玻尔兹曼机 深度卷积网络 理解图像内容以及图像含义方面&#xff0c;计算机遇到了很大困难。本章先介绍计算机理解图像教育方面 遇到的难题&#xff0c;接着重点讲解一个基于深度学习的解决方法。我们会…

51单片机——按键实验

由于机械点的弹性作用&#xff0c;按键开关在闭合时不会马上稳定的接通&#xff0c;在断开时也不会一下子断开&#xff0c;因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的&#xff0c;一般为 5ms 到 10ms&#xff0c;为了确保 CPU 对按键的…

关于嵌入式系统的知识课堂(二)

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于嵌入式系统的知识课堂&#xff08;…

CSS——5. 外部样式

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>方法3&#xff1a;外部样式</title><link rel"stylesheet" href"a.css" /><link rel"stylesheet" href"b.css"/&g…

玩游戏提示找不到vcruntime140.dll,修复方法分享

在玩游戏时&#xff0c;电脑突然弹出“找不到vcruntime140.dll”的提示怎么回事&#xff1f;玩游戏时提示找不到vcruntime140.dll文件时&#xff0c;这通常意味着你的系统中缺少了这个关键的DLL文件&#xff0c;或者该文件已损坏。vcruntime140.dll是Visual C运行时库的一部分&…

Aloudata AIR | 逻辑数据平台的 NoETL 之道

一文为你介绍 Aloudata AIR 逻辑数据平台的技术原理与核心价值 本文主旨是介绍逻辑数据平台的技术原理与核心价值&#xff0c;包含几个部分的内容&#xff1a; 首先&#xff0c;简要阐述逻辑数据平台出现的背景&#xff1b;其次&#xff0c;详细讲解逻辑数据平台的构建方法&am…

电子应用设计方案86:智能 AI背景墙系统设计

智能 AI 背景墙系统设计 一、引言 智能 AI 背景墙系统旨在为用户创造一个动态、个性化且具有交互性的空间装饰体验&#xff0c;通过融合先进的技术和创意设计&#xff0c;提升室内环境的美观度和功能性。 二、系统概述 1. 系统目标 - 提供多种主题和风格的背景墙显示效果&…

12306分流抢票软件 bypass v1.16.43 绿色版(春节自动抢票工具)

软件介绍 12306Bypass分流抢票软件&#xff0c;易操作强大的12306抢票软件&#xff0c;全程自动抢票&#xff0c;云识别验证码打码&#xff0c;多线程秒单、稳定捡漏&#xff0c;支持抢候补票、抢到票自动付款&#xff0c;支持多天、多车次、多席别、多乘客、短信提醒等功能。…

MiFlash 线刷工具下载合集

MiFlash 线刷工具下载合集 MiFlash 线刷工具下载合集 – MIUI历史版本相较于小米助手的刷机功能&#xff0c;线刷还是偏好使用 MiFlash。特点是界面简单纯粹&#xff0c;有自定义高级选项&#xff0c;可以选择刷机不上 BL 锁&#xff0c;自定义刷机脚本&#xff0c;EDL 刷机模…

面向对象分析和设计OOA/D,UML,GRASP

目录 什么是分析和设计&#xff1f; 什么是面向对象的分析和设计&#xff1f; 迭代开发 UML 用例图 交互图 基于职责驱动设计 GRASP 常见设计原则 什么是分析和设计&#xff1f; 分析&#xff0c;强调是对问题和需求的调查研究&#xff0c;不是解决方案。例如&#x…

第五届电网系统与绿色能源国际学术会议(PGSGE 2025)

2025年第五届电网系统与绿色能源国际学术会议(PGSGE 2025) 定于2025年01月10-12日在吉隆坡召开。 第五届电网系统与绿色能源国际学术会议&#xff08;PGSGE 2025&#xff09; 基本信息 会议官网&#xff1a;www.pgsge.org【点击投稿/了解会议详情】 会议时间&#xff1a;202…