蓝桥杯-单片机组基础11——基于2023年官方iic通信代码讲解(模数转换器PCF8591)

蓝桥杯单片机组备赛指南请查看 :本专栏第1篇文章

本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。

型号:国信天长4T开发板(绿板),芯片:IAP15F2K61S2

(使用国信天长蓝板也可以完美兼容,与绿板几乎无差别)


比赛用开发板上,使用iic通信的外设有两个:PCF8591,AT24C02。另外一个的讲解文章参考该专栏下一篇

1. 代码目的

        通过对PCF8591的正确设置,可以正确的读取开发板上的光敏电阻rd1和可调滑动电阻rb2的对应数值,并通过按键S7切换显示数据

2. iic基础知识讲解

         IIC总线全称:Inter-Integrated Circuit,是由飞利浦公司开发出来的一种串行总线协议,它是一种多主机的总线,当发生主机竞争时,有总线仲裁机制

           IIC总线只有2根信号线,一根是数据线SDA,一根是时钟线SCL。SDA和SCL均为双向信号线,通过上拉电阻接正电源。当总线空闲时,两根线都是高电平。连接到总线上的任一器件,输出低电平,都将使总线的信号变低。
      连接总线的器件输出级必须是集电极或漏极开路,以形成线“与”功能
      每个具有IIC接口的设备都有一个唯一的地址,也叫做设备地址

3. PCF8951芯片讲解

对该芯片的操作,粗略的介绍就是:

        1.发送写设备地址,选中PCF8951

        2.发送对应器件的控制字节,选择模拟量的输入端口

        3.发送读设备地址,选中PCF8951,AD转换在应答时钟脉冲的后延触发

        4.读取对应端口的数据

        我们将上方的芯片原理图,与开发板硬件连接图进行对比分析。开发板硬件原理图如下:

读写地址:

        对比上方两个原理图,发现A0,A1,A2全部接地,为000,则读与写只由lsb的最后一位决定,当R/W赋值1则读取数据,赋值0则写入数据。结合msb与lsb内容,当我们需要读取数据时,则写入0x81;需要写入数据时,则写入0x80

控制字节:

PCF8591在蓝桥杯开发板上共有4个输入端口:AIN0,AIN1,AIN2,AIN3

比赛要使用的,一般情况下只有 : AIN1->rd1,0x01;  AIN3->rb2,0x03

        AIN0: 0x00,为外部模数信号控制端。可直接读取外部的模拟信号或数字信号,比赛基本不考

        AIN1:0x01,为光敏传感器rd1信号输入端。可以获取到光敏电阻获取的光照数值

        AIN2:0x02,为内部差分放大电路信号端。可以用于驱动外部电路,比赛一般不考

        AIN3:0x03,为可调滑动变阻器rb2的输入端。可以查看滑动电阻的数值

4. iic底层文件处理(添加头文件方式)

         官方提供了底层iic.c文件,我们可以直接采用,对PCF8951进行正确的通信设置。但是,从16年开始为了增加难度,代码会故意出现错误与遗漏,我们以2023年官方提供的底层文件为参考

        下载链接:链接:https://pan.baidu.com/s/1LfixDiinqsOhYbhrAptbMQ      提取码:1111

步骤一:创建头文件iic.h

        在keil5左侧工程导航栏中,在source group处右键点击“add new items”到工程:

        在新建的iic.h文件中,添加以下代码:

#ifndef __IIC_H__
#define __IIC_H__

void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(unsigned char ackbit);

#endif

步骤二:定义iic.c原函数文件

        在keil5左侧工程导航栏中,在source group处右键点击“add existing items”到工程,并选中官方提供的底层文件:

        此时我们的左侧工程栏就会出现一个新的文件,我们双击打开,并查看原理图:

        因此我们需要在iic.c文件中添加如下代码:

步骤三:在主函数中添加我们新建的头文件

        至此,对于头文件的操作结束。最终我们的iic.c代码的全部展示如下:

/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <reg52.h>
#include <intrins.h>

sbit sda = P2^1;
sbit scl = P2^0;

#define DELAY_TIME	5

//
static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();		
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}

5. 程序时序流程

        更新:在查阅了众多博客后我发现,在第6步之后添加一个1~2ms的延时,效果会更好一些

否则光敏电阻的数值总是会产生跳动。加上一点延时后跳动消失。

对于rd1和rb2的操作时序,完全一致,只需要写入不同的控制字节,既可读取不同的数据。

因此我们只需要记住1~12个步骤,在比赛时利用官方提供的驱动文件,自己写出调用函数即可。

操作时,时序错误可能发生在一个地方,即第11步产生非应答信号处。不同的底层代码产生非应答信号的实参不同,我们去观察源程序:

根据iic时序要求,当scl拉低为低电平时,将sda保持到高电平产生非应答信号,保持到低电平时产生应答信号。

产生非应答信号:ackbit=1

产生应答信号:ackbit=0

具体比赛时需要根据官方给的源码,选择不同的形参

6. 外设代码参考

7. 代码参考

#include <reg52.h>
#include <intrins.h>
#include "iic.h"
 
sbit S7 = P3^0;
 
unsigned char code duanma [18]={ 0xc0 , 0xf9 , 0xa4 , 0xb0 , 0x99 , 0x92 , 0x82 , 0xf8 , 0x80 , 0x90 , 0x88 ,
												0x80 , 0xc6 , 0xc0 , 0x86 , 0x8e ,0xbf , 0x7f };
 
void SMGrunning ();
				
void select_HC573 ( unsigned char channal )
{
	switch ( channal )
	{
		case 4:
			P2 = ( P2 & 0x1f ) | 0x80;
		break;
		case 5:
			P2 = ( P2 & 0x1f ) | 0xa0;
		break;
		case 6:
			P2 = ( P2 & 0x1f ) | 0xc0;
		break;
		case 7:
			P2 = ( P2 & 0x1f ) | 0xe0;
		break;
	}
}
 
void state_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{
	select_HC573( 7 );
	P0 = 0x01 << 0xff;
	
	select_HC573( 6 );
	P0 = 0x01 << pos_SMG;
	select_HC573( 7 );
	P0 = value_SMG;
}
 
void state_SMG_all ( unsigned char value_SMG )
{
	select_HC573( 6 );
	P0 = 0xff;
	select_HC573( 7 );
	P0 = value_SMG;
}
 
void init_sys ()
{
	select_HC573( 4 );
	P0 = 0xff;
	select_HC573( 5 );
	P0 = 0x00;
}
 
 
void Delay2ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 22;
	j = 128;
	do
	{
		while (--j);
	} while (--i);
}

 
void Delay20ms()		//@11.0592MHz
{
	unsigned char i, j;
 
	i = 216;
	j = 37;
	do
	{
		while (--j);
	} while (--i);
}
 
unsigned char key_state = 1;
void keyrunning ()
{
	if ( S7 == 0 )
	{
		Delay20ms();
		if ( S7 == 0 )
		{
			while ( S7 == 0 )
			{
				SMGrunning();
			}
			
			if ( key_state == 3 )
			{
				key_state = 1;
			}
			else
			{
				key_state = 3;
			}
		}
	}
}
 
 
unsigned char rb2_value = 0;
void rb2running()
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x03);
	I2CWaitAck();
	I2CStop();
	
	Delay2ms();
		
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	rb2_value = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	
}
 
unsigned char rd1_value = 0;
void rd1running()
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(0x01);
	I2CWaitAck();
	I2CStop();
	
	Delay2ms();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	rd1_value = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
}
 
 
void SMGrunning ( )
{
	if ( key_state == 1 )
	{		
		state_SMG ( 0 , duanma[16] );
		Delay2ms();
		state_SMG ( 1 , duanma[key_state] );
		Delay2ms();
		state_SMG ( 2 , duanma[16] );
		Delay2ms();
		state_SMG ( 3 , 0xff );
		Delay2ms();
		state_SMG ( 4 , 0xff );
		Delay2ms();
		state_SMG ( 5 , duanma[rd1_value/100] );
		Delay2ms();
		state_SMG ( 6 , duanma[rd1_value/10%10] );
		Delay2ms();
		state_SMG ( 7 , duanma[rd1_value%10] );
		Delay2ms();
	}
	else if ( key_state == 3 )
	{
		state_SMG ( 0 , duanma[16] );
		Delay2ms();
		state_SMG ( 1 , duanma[key_state] );
		Delay2ms();
		state_SMG ( 2 , duanma[16] );
		Delay2ms();
		state_SMG ( 3 , 0xff );
		Delay2ms();
		state_SMG ( 4 , 0xff );
		Delay2ms();
		state_SMG ( 5 , duanma[rb2_value/100] );
		Delay2ms();
		state_SMG ( 6 , duanma[rb2_value/10%10] );
		Delay2ms();
		state_SMG ( 7 , duanma[rb2_value%10] );
		Delay2ms();
	}		
 
	state_SMG_all ( 0xff );
}
 
void main ()
{
	init_sys ();
	while ( 1 )
	{
		keyrunning ();
		SMGrunning ();
		if( key_state == 1 )
		{
			rd1running();
		}
		else
		{
			rb2running();
		}
		
	}
}

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值