• ARM接口编程—IIC总线(exynos 4412平台)


    IIC总线简介

    • IIC总线是Philips公司在八十年代初推出的一种串行、半双工总线
      主要用于近距离、低速的芯片之间的通信;IIC总线有两根双向的信号线一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步;IIC总线硬件结构简单,成本较低,因此在各个领域得到了广泛的应用

    • IIC总线是一种多主机总线,连接在IIC总线上的器件分为主机和从机主机有权发起和结束一次通信,而从机只能被主机呼叫;当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生;
      每个连接到IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(同一时刻只能有一个主机),总线上的器件增加和删除不影响其他器件正常工作;IIC总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器;

    IIC总线通信过程

    • 1.主机发送起始信号启用总线
    • 2.主机发送一个字节数据指明从机地址和后续字节的传送方向
    • 3.被寻址的从机发送应答信号回应主机
    • 4.发送器发送一个字节数据
    • 5.接收器发送应答信号回应发送器
    • … … (循环步骤4、5)
    • n.通信完成后主机发送停止信号释放总线

    IIC总线寻址方式

    • IIC总线上传送的数据是广义的,既包括地址,又包括真正的数据
    • 主机在发送起始信号后必须先发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,'0’表示主机发送数据,'1’表示主机接收数据;总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定为发送器或接收器

    起始信号和停止信号

    • SCL为高电平时,SDA由高变低表示起始信号
    • SCL为高电平时,SDA由低变高表示停止信号
    • 起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态
      停止信号产生后总线处于空闲状态

    在这里插入图片描述

    字节传送与应答

    IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器即一帧共有9位

    在这里插入图片描述

    同步信号

    IIC总线在进行数据传送时,时钟线SCL为低电平期间发送器向数据线上发送一位数据,在此期间数据线上的信号允许发生变化,时钟线SCL为高电平期间接收器从数据线上读取一位数据,在此期间数据线上的信号不允许发生变化,必须保持稳定

    在这里插入图片描述

    典型IIC时序

    • 主机向从机发送数据
      在这里插入图片描述

    • 从机向主机发送数据
      在这里插入图片描述

    • 主机先向从机发送数据,然后从机再向主机发送数据

    在这里插入图片描述

    注:阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送;A表示应答, A非表示非应答,S表示起始信号,P表示终止信号

    代码实现

    #include "exynos_4412.h"
    
    /****************MPU6050内部寄存器地址****************/
    
    #define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
    #define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
    #define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
    #define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
    #define	ACCEL_XOUT_H	0x3B
    #define	ACCEL_XOUT_L	0x3C
    #define	ACCEL_YOUT_H	0x3D
    #define	ACCEL_YOUT_L	0x3E
    #define	ACCEL_ZOUT_H	0x3F
    #define	ACCEL_ZOUT_L	0x40
    #define	TEMP_OUT_H		0x41
    #define	TEMP_OUT_L		0x42
    #define	GYRO_XOUT_H		0x43
    #define	GYRO_XOUT_L		0x44
    #define	GYRO_YOUT_H		0x45
    #define	GYRO_YOUT_L		0x46
    #define	GYRO_ZOUT_H		0x47
    #define	GYRO_ZOUT_L		0x48
    #define	PWR_MGMT_1		0x6B	//电源管理,典型值:0x00(正常启用)
    #define	WHO_AM_I		0x75	//IIC地址寄存器(默认数值0x68,只读)
    #define	SlaveAddress	0x68	//MPU6050-I2C地址
    
    void mydelay_ms(int time)
    {
    	int i,j;
    	while(time--)
    	{
    		for(i=0;i<5;i++)
    			for(j=0;j<514;j++);
    	}
    }
    
    void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
    {
    	/*对时钟源进行512倍预分频  打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
    	I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
    
    	/*设置IIC模式为主机发送模式  使能IIC发送和接收*/
    	I2C5.I2CSTAT = 0xd0;
    	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位(MPU6050-I2C地址+写位0)*/
    	I2C5.I2CDS = slave_addr<<1;
    	/*设置IIC模式为主机发送模式  发送起始信号启用总线  使能IIC发送和接收*/
    	I2C5.I2CSTAT = 0xf0;
    
    	/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
    	while(!(I2C5.I2CCON & (1<<4)));
    
    	/*将要发送的第二个字节数据(即MPU6050内部寄存器的地址)写入发送寄存器*/
    	I2C5.I2CDS = addr;
    	/*清除中断挂起标志位  开始下一个字节的发送*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    	/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
    	while(!(I2C5.I2CCON & (1<<4)));
    
    	/*将要发送的第三个字节数据(即要写入到MPU6050内部指定的寄存器中的数据)写入发送寄存器*/
    	I2C5.I2CDS = data;
    	/*清除中断挂起标志位  开始下一个字节的发送*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    	while(!(I2C5.I2CCON & (1<<4)));
    
    	/*发送停止信号  结束本次通信*/
    	I2C5.I2CSTAT = 0xD0;
    	/*清除中断挂起标志位*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    	/*延时*/
    	mydelay_ms(10);
    }
    
    unsigned char iic_read(unsigned char slave_addr, unsigned char addr)
    {
    
    	unsigned char data = 0;
    
    	/*对时钟源进行512倍预分频  打开IIC中断(每次完成一个字节的收发后中断标志位会自动置位)*/
    	I2C5.I2CCON = I2C5.I2CCON | (1<<6) | (1<<5);
    
    	/*设置IIC模式为主机发送模式  使能IIC发送和接收*/
    	I2C5.I2CSTAT = 0xd0;
    	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位(MPU6050-I2C地址+写位0)*/
    	I2C5.I2CDS = slave_addr<<1;
    	/*设置IIC模式为主机发送模式  发送起始信号启用总线  使能IIC发送和接收*/
    	I2C5.I2CSTAT = 0xf0;
    	/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
    	while(!(I2C5.I2CCON & (1<<4)));
    
    	/*将要发送的第二个字节数据(即要读取的MPU6050内部寄存器的地址)写入发送寄存器*/
    	I2C5.I2CDS = addr;
    	/*清除中断挂起标志位  开始下一个字节的发送*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    	/*等待从机接受完一个字节后产生应答信号(应答后中断挂起位自动置位)*/
    	while(!(I2C5.I2CCON & (1<<4)));
    	/*清除中断挂起标志位  重新开始一次通信  改变数据传送方向*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    
    	/*将第一个字节的数据写入发送寄存器  即从机地址和读写位(MPU6050-I2C地址+读位1)*/
    	I2C5.I2CDS = slave_addr << 1 | 0x01;
    	/*设置IIC为主机接收模式  发送起始信号  使能IIC收发*/
    	I2C5.I2CSTAT = 0xb0;
    	/*等待从机接收到数据后应答*/
    	while(!(I2C5.I2CCON & (1<<4)));
    	
    	/*禁止主机应答信号(即开启非应答  因为只接收一个字节)  清除中断标志位*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<7))&(~(1<<4));
    	/*等待接收从机发来的数据*/
    	while(!(I2C5.I2CCON & (1<<4)));
    	/*将从机发来的数据读取*/
    	data = I2C5.I2CDS;
    
    	/*直接发起停止信号结束本次通信*/
    	I2C5.I2CSTAT = 0x90;
    	/*清除中断挂起标志位*/
    	I2C5.I2CCON = I2C5.I2CCON & (~(1<<4));
    	/*延时等待停止信号稳定*/
    	mydelay_ms(10);
    
    	return data;
    }	
    
    void MPU6050_Init ()
    {
    	iic_write(SlaveAddress, PWR_MGMT_1, 0x00); 		//设置使用内部时钟8M
    	iic_write(SlaveAddress, SMPLRT_DIV, 0x07);		//设置陀螺仪采样率
    	iic_write(SlaveAddress, CONFIG, 0x06);			//设置数字低通滤波器
    	iic_write(SlaveAddress, GYRO_CONFIG, 0x18);		//设置陀螺仪量程+-2000度/s
    	iic_write(SlaveAddress, ACCEL_CONFIG, 0x0);		//设置加速度量程+-2g
    }
    
    int main(void)
    {
    
    	unsigned char zvalue_h,zvalue_l;						//存储读取结果
    	short int zvalue;
    
    	/*设置GPB_2引脚和GPB_3引脚功能为I2C传输引脚*/
    	GPB.CON = (GPB.CON & ~(0xF<<12)) | 0x3<<12;			 	//设置GPB_3引脚功能为I2C_5_SCL
    	GPB.CON = (GPB.CON & ~(0xF<<8))  | 0x3<<8;				//设置GPB_2引脚功能为I2C_5_SDA
    
    	uart_init(); 											//初始化串口
    	MPU6050_Init();											//初始化MPU6050
    
    	printf("\n********** I2C test!! ***********\n");
    	while(1)
    	{
    		zvalue_h = iic_read(SlaveAddress, GYRO_ZOUT_H);		//获取MPU6050-Z轴角速度高字节
    		zvalue_l = iic_read(SlaveAddress, GYRO_ZOUT_L);		//获取MPU6050-Z轴角速度低字节
    		zvalue  =  (zvalue_h<<8)|zvalue_l;					//获取MPU6050-Z轴角速度
    
    		printf(" GYRO--Z  :Hex: %d	\n", zvalue);			//打印MPU6050-Z轴角速度
    		mydelay_ms(100);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
  • 相关阅读:
    哈希表 or HashMap模拟实现
    【ROS2原理17】ROS 2 安全飞地
    【机器学习合集】激活函数合集 ->(个人学习记录笔记)
    解决vue-cli node-sass安装不成功问题
    常用Linux命令详细总结
    突破编程_C++_高级教程(单元测试与 Google Test 教程)
    【Docker】之安装 RabbitMQ
    集成学习的小九九
    松哥手把手教你在 Vue3 中自定义插件
    神经网络时间序列分析,神经网络模型可解释性
  • 原文地址:https://blog.csdn.net/hhltaishuai/article/details/132831792