STC8系列单片机硬件I2C使用教程(一)

该博客介绍了如何使用STC8F2K08S2单片机进行I2C通信的硬件配置和软件编程,包括寄存器设置、起始和停止信号、数据发送与接收等。同时,提供了BMP085温压传感器的读取例程,实现了温度和压力的读取并转换为ASCII字符输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



一、I2C相关的寄存器

I2C相关的寄存器

① I2C 配置寄存器

在这里插入图片描述


② I2C 主机控制寄存器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


③ I2C 主机辅助控制寄存器

在这里插入图片描述

④ I2C 主机状态寄存器

在这里插入图片描述
在这里插入图片描述


⑤ I2C 数据寄存器

在这里插入图片描述

⑥ 外设端口切换控制寄存器 1

在这里插入图片描述


⑦ 外设端口切换控制寄存器 2

在这里插入图片描述




二、程序编写

① 寄存器和相关宏定义

sfr P_SW2 = 0xBA;		//外设端口切换寄存器 2

#define I2CCFG		(*(unsigned char volatile xdata *)0xfe80)
#define I2CMSCR		(*(unsigned char volatile xdata *)0xfe81)
#define I2CMSST		(*(unsigned char volatile xdata *)0xfe82)
#define I2CTXD		(*(unsigned char volatile xdata *)0xfe86)
#define I2CRXD		(*(unsigned char volatile xdata *)0xfe87)

#define I2C_S0	0x10
#define I2C_S1	0x20
#define EAXFR	0x80		//I2C功能寄存器为扩展 SFR,逻辑地址位于 XDATA 区域,访问前需要将 P_SW2(BAH)寄存器的最高位(EAXFR)置 1

sbit SDA = P3^3;
sbit SCL = P3^2;

#ifndef uchar
	#define uchar unsigned char
#endif
#ifndef uint
	#define uint  unsigned int
#endif

② 硬件I2C初始化

void init_i2c(void)
{
	uchar temp = 0x00;							
	
//										//切换到第一组I2C
//	temp &= ~(I2C_S0 | I2C_S1);			//I2C_S0=0 I2C_S1=0
//										//(P1.5/SCL, P1.4/SDA)

//										//切换到第二组I2C
//	temp &= ~(I2C_S0 | I2C_S1);			//I2C_S0=1 I2C_S1=0
//	temp |= I2C_S0;						//(P2.5/SCL, P2.4/SDA)

//										//切换到第三组I2C
//	temp &= ~(I2C_S0 | I2C_S1);			//I2C_S0=0 I2C_S1=1
//	temp |= I2C_S1;						//(P7.7/SCL, P7.6/SDA)
	
										//切换到第四组I2C
	temp |= (I2C_S0 | I2C_S1);			//I2C_S0=1 I2C_S1=1
										//(P3.2/SCL, P3.3/SDA)
	
	temp |= EAXFR;						//I2C功能寄存器为扩展 SFR,逻辑地址位于 XDATA 区域,访问前需要将 P_SW2(BAH)寄存器的最高位(EAXFR)置 1
	
	P_SW2 = temp;
	

	I2CCFG = 0xE0;                              //使能I2C主机模式
	I2CMSST = 0x00;
}

③ 硬件I2C操作代码

void wait()
{
    while(!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;
}

void start()
{
    I2CMSCR = 0x01;                             //发送START命令
    wait();
}

void send_data(uchar dat)
{
    I2CTXD = dat;                               //写数据到数据缓冲区
    I2CMSCR = 0x02;                             //发送SEND命令
    wait();
}

void recv_ack()
{
    I2CMSCR = 0x03;                             //发送读ACK命令
    wait();
}

uchar recv_data()
{
    I2CMSCR = 0x04;                             //发送RECV命令
    wait();
    return I2CRXD;
}

void send_ack()
{
    I2CMSST = 0x00;                             //设置ACK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    wait();
}

void send_nack()
{
    I2CMSST = 0x01;                             //设置NAK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    wait();
}

void stop()
{
    I2CMSCR = 0x06;                             //发送STOP命令
    wait();
}

④ BMP085 读写例程

#define	BMP085_SlaveAddress   	0xEE		//定义器件在IIC总线中的从地址

#define OSS                 0
#define p0                  101325.0

uchar send[6];

xdata short ac1;
xdata short ac2; 
xdata short ac3; 
xdata unsigned short ac4;
xdata unsigned short ac5;
xdata unsigned short ac6;
xdata short b1; 
xdata short b2;
xdata short mb;
xdata short mc;
xdata short md;

//10进制数字转ASCII字符
void Conversion(long temp_data, uchar *pAscii)
{  
    *(pAscii + 5) = temp_data / 100000 + 0x30;		//十万
    temp_data = temp_data % 100000;					//取余运算 
    *(pAscii + 4) = temp_data / 10000 + 0x30;		//万
    temp_data = temp_data % 10000;					//取余运算
	*(pAscii + 3) = temp_data / 1000 + 0x30;		//千
    temp_data = temp_data % 1000;					//取余运算
    *(pAscii + 2) = temp_data / 100 + 0x30;			//百
    temp_data = temp_data % 100;					//取余运算
    *(pAscii + 1) = temp_data / 10 + 0x30;			//十
    temp_data = temp_data % 10;						//取余运算
    *(pAscii + 0) = temp_data + 0x30;				//个
}

//单字节写入
void Single_Write(uchar SlaveAddress, uchar REG_Address, uchar REG_data)
{
    start();					//起始信号
    send_data(SlaveAddress);	//发送设备地址+写信号
	recv_ack();
    send_data(REG_Address);		//写寄存器地址
	recv_ack();	
    send_data(REG_data);		//写寄存器数据
	recv_ack();
    stop();						//发送停止信号
}

//单字节读取
uchar Single_Read(uchar SlaveAddress, uchar REG_Address)
{  
	uchar REG_data;
    start();                          //起始信号
    send_data(SlaveAddress);           //发送设备地址+写信号
	recv_ack();
    send_data(REG_Address);            //写寄存器地址
	recv_ack();
    start();                          //起始信号
    send_data(SlaveAddress + 1);         //发送设备地址+读信号
	recv_ack();
    REG_data = recv_data();              //读出寄存器数据
	send_nack();   
	stop();                           //停止信号
    return REG_data; 
}

//读出BMP085内部数据,连续两个
short Multiple_read(uchar SlaveAddress, uchar ST_Address)
{   
	uchar msb, lsb;
	short _data;
    start();                          //起始信号
    send_data(SlaveAddress);    //发送设备地址+写信号
	recv_ack();
    send_data(ST_Address);             //写寄存器地址
	recv_ack();
    start();                          //起始信号
    send_data(SlaveAddress + 1);         //发送设备地址+读信号
	recv_ack();

    msb = recv_data();                 //BUF[0]存储
    send_ack();                       //回应ACK
    lsb = recv_data();     
	send_nack();                      //最后一个数据需要回NACK

    stop();                           //停止信号
    Delay5ms();
    _data = msb << 8;
	_data |= lsb;	
	return _data;
}

//BMP085读温度
long BMP085_Read_Temp(void)
{
	Single_Write(BMP085_SlaveAddress, 0xF4, 0x2E);
	Delay5ms();	//最大时间4.5ms
	return (long)Multiple_read(BMP085_SlaveAddress, 0xF6);
}
//BMP085读压力
long BMP085_Read_Pressure(void)
{
	long pressure = 0;

	Single_Write(BMP085_SlaveAddress, 0xF4, 0x34);
	Delay5ms();    	                  //最大时间4.5ms
	
	pressure = Multiple_read(BMP085_SlaveAddress, 0xF6);
	pressure &= 0x0000FFFF;
	
	return pressure;
}

//初始化BMP085,根据需要请参考pdf进行修改
void Init_BMP085()
{
	ac1 = Multiple_read(BMP085_SlaveAddress, 0xAA);
	ac2 = Multiple_read(BMP085_SlaveAddress, 0xAC);
	ac3 = Multiple_read(BMP085_SlaveAddress, 0xAE);
	ac4 = Multiple_read(BMP085_SlaveAddress, 0xB0);
	ac5 = Multiple_read(BMP085_SlaveAddress, 0xB2);
	ac6 = Multiple_read(BMP085_SlaveAddress, 0xB4);
	b1 =  Multiple_read(BMP085_SlaveAddress, 0xB6);
	b2 =  Multiple_read(BMP085_SlaveAddress, 0xB8);
	mb =  Multiple_read(BMP085_SlaveAddress, 0xBA);
	mc =  Multiple_read(BMP085_SlaveAddress, 0xBC);
	md =  Multiple_read(BMP085_SlaveAddress, 0xBE);
}
//开启转换
void BMP085_Convert()
{
	long ut;
	long up;
	long x1, x2, b5, b6, x3, b3, p;
	unsigned long b4, b7;
	long temperature;
	long pressure;
	double altitude;
	
	ut = BMP085_Read_Temp();
	//ut = BMP085_Read_Temp();	   // 读取温度
	up = BMP085_Read_Pressure();
	//up = BMP085_Read_Pressure();  // 读取压强
	
	x1 = ((long)ut - ac6) * ac5 >> 15;
	x2 = ((long) mc << 11) / (x1 + md);
	b5 = x1 + x2;
	temperature = (b5 + 8) >> 4;
	
	Conversion(temperature, send);
	uart_sendstring("Temperature: ");       //温度显示
	uart_sendchar(send[2]); 
	uart_sendchar(send[1]); 
	uart_sendchar('.'); 
    uart_sendchar(send[0]);
	uart_sendstring("℃\r\n");//温度单位
	memset(send, 0x00, 6);
	
	b6 = b5 - 4000;
	x1 = (b2 * (b6 * b6 >> 12)) >> 11;
	x2 = ac2 * b6 >> 11;
	x3 = x1 + x2;
	b3 = (((long)ac1 * 4 + x3) + 2)/4;
	x1 = ac3 * b6 >> 13;
	x2 = (b1 * (b6 * b6 >> 12)) >> 16;
	x3 = ((x1 + x2) + 2) >> 2;
	b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15;
	b7 = ((unsigned long) up - b3) * (50000 >> OSS);
	if( b7 < 0x80000000)
	     p = (b7 * 2) / b4 ;
           else  
		    p = (b7 / b4) * 2;
	x1 = (p >> 8) * (p >> 8);
	x1 = (x1 * 3038) >> 16;
	x2 = (-7357 * p) >> 16;
		   
	pressure = p + ((x1 + x2 + 3791) >> 4);
	Conversion(pressure, send);
	uart_sendstring("Pressure: ");       //显示压强
	uart_sendchar(send[5]); 
	uart_sendchar(send[4]); 
	uart_sendchar(send[3]); 
	uart_sendchar('.'); 
    uart_sendchar(send[2]);
	uart_sendchar(send[1]); 
	uart_sendchar(send[0]); 
	uart_sendstring("kPa\r\n");//压强单位
	memset(send, 0x00, 6);
	
	
	altitude = 44330.0 * (1 - pow((float)pressure / p0, 1.0 / 5.255)) * 100;//精度cm
	Conversion(altitude, send);
	uart_sendstring("Altitude:");    //显示海拔
	uart_sendchar(send[4]); 
	uart_sendchar(send[3]);
    uart_sendchar(send[2]);
	uart_sendchar('.'); 
	uart_sendchar(send[1]); 
	uart_sendchar(send[0]); 
	uart_sendstring("m\r\n");//海拔单位
	memset(send, 0x00, 6);
}
//主函数
void main(void)
{
	init_uart();
	
	init_i2c();
	
	ES = 1;		//使能串口中断
	EA = 1;		//使能总中断

	Init_BMP085();
	
	while(1){
		BMP085_Convert();
		uart_sendstring("\r\n");
		uart_sendstring("\r\n");
		uart_sendstring("\r\n");
		Delay500ms();
		Delay500ms();
		Delay500ms();
		Delay500ms();
	}
}

⑤ 串口代码

#ifndef FOSC
	#define FOSC 24000000L		//系统频率24MHz
#endif
#define BAUD 115200           //UART波特率

sfr P_SW1 = 0xa2;
sfr AUXR  = 0x8e;
sfr T2H   = 0xd6;
sfr T2L   = 0xd7;

bit busy;

//初始化串口
void init_uart()
{
	P_SW1 = 0x00;                               //RXD/P3.0, TXD/P3.1
//  P_SW1 = 0x40;                               //RXD_2/P3.6, TXD_2/P3.7
//  P_SW1 = 0x80;                               //RXD_3/P1.6, TXD_3/P1.7
//  P_SW1 = 0xc0;                               //RXD_4/P4.3, TXD_4/P4.4
	
	SCON = 0x50;
    T2L  = (65536 - FOSC / BAUD / 4) % 256;     //65536 - FOSC / BAUD / 4
    T2H  = (65536 - FOSC / BAUD / 4) / 256;
    AUXR = 0x15;                                //启动定时器
}

//串口发送单个字符
void uart_sendchar(uchar dat)
{
    while (busy);
    busy = 1;
    SBUF = dat;
}

//串口发送字符串
void uart_sendstring(uchar *p)
{
    while(*p){
        uart_sendchar(*p++);
    }
}

//串口接收中断
void uart_isr() interrupt 4 using 1
{
    if(TI){
        TI = 0;
        busy = 0;
    }
    if(RI){
        RI = 0;
    }
}

模拟电压转I2C电路是种用于将模拟电压信号转换为I2C(Inter-Integrated Circuit)总线信号的电路。I2C种常用的串行通信协议,广泛应用于微控制器和传感器之间的通信。模拟电压转I2C电路通常用于将模拟传感器的输出信号转换为数字信号,以便微控制器进行处理。 ### 主要组成部分 1. **模拟电压输入**:来自传感器的模拟电压信号。 2. **模数转换器(ADC)**:将模拟电压信号转换为数字信号。 3. **微控制器**:处理数字信号并生成I2C通信协议。 4. **I2C总线接口**:将处理后的信号通过I2C总线传输到其他设备。 ### 工作原理 1. **模拟电压输入**:传感器将物理量(如温度、压力等)转换为模拟电压信号。 2. **模数转换**:ADC将模拟电压信号转换为数字信号。ADC的分辨率和采样率决定了转换的精度和速度。 3. **数据处理**:微控制器接收ADC的数字信号,并根据需要进行数据处理,如滤波、校准等。 4. **I2C通信**:微控制器通过I2C总线接口将处理后的数据发送给其他设备,如显示器、存储设备或其他微控制器。 ### 示例电路 以下是个简单的模拟电压转I2C电路的示例: ```plaintext 传感器 ---- 模拟电压输入 ---- ADC ---- 微控制器 ---- I2C总线 ---- 其他设备 ``` ### 应用场景 - **传感器数据采集**:用于将各种传感器的模拟输出转换为数字信号,便于数据处理和传输。 - **工业自动化**:在工业控制系统中,模拟电压转I2C电路用于采集传感器数据并通过I2C总线传输。 - **消费电子产品**:如智能家居设备中的温度、湿度传感器等。 ### 优点 - **简化电路设计**:通过I2C总线,可以减少信号线的数量,简化电路设计。 - **便于扩展**:多个设备可以通过I2C总线进行通信,方便系统扩展。 - **高效的数据传输**:I2C总线支持较高的数据传输速率,适合实时数据采集和处理。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菠萝蚊鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值