文章目录

前言

上一篇主要介绍了一些关于stc-isp的使用技巧,此篇继续手撕选手资源包的芯片手册部分。

DS18B20

原理图

DS18B20是常用1-Wire协议的器件之一,1-Wire见名知意,单线即可完成主从通信,所以主机和从机只需要一根线连接即可;1-Wire数据端口为漏极开路构或三态门的端口,因此一般需要加上拉电阻Rp,通常选用5K~10KΩ。

1-Wire协议简介

单总线协议(1-Wire):单总线采用单根信号线实现数据双向传输,数据传输次序为低八位在前高八位在后。

DS18B20和单片机之间是主从通信,只有主机给从机命令后从机才能发送数据;从机并不能主动发送数据。
想要仔细了解DS18B20通信的同学可以参考此文——DS18B20温度传感器的特性、原理、驱动

赛场技巧

使用此协议的器件通讯过程一般遵循一下步骤:

1.初始化总线;2.ROM命令(可以理解为进场的身份核实);3.功能命令(可理解为需要芯片执行的任务指令);
记住三个步骤:1 init 2 ROM 3 配功能;记住这个,然后学会看手册就可以配置出比赛要用的代码了。
首选大致扫一眼下面的指令表。

然后是手册,此篇的主角,官方资源里面的芯片手册是纯英语的,但是这都不是事,我们只需要记住一页就行了,当然备赛还是建议大家还是通篇看一看,现在直接找到DS18B20数据手册18页。

这是官方的一个读取温度流程,上面的表格是总线有多个DS18B20的配置方式,下面一个表格是当总线上只有有一个DS18B20的配置方式,按说是应该使用下面的这个表格的,但是官方配置了其他(4E这些)寄存器,在比赛过程中我们使用不到,为了简便就借用第一个表格来辅助记忆整个获取温度的流程。
注意表中第3、4、5、9、10、11行的Presence、55h以及64-bit ROM code这三步是有关应答和判断DS18B20的ROM匹配的,后者因为总线上只有一个器件,就可以写入CC H直接跳过ROM。
(大家仔细对比官方配置模式以及读写时序就会发现我们在配置过程中丢弃了好多应答类的步骤,用的是一个小延时给替代了,不过对于比赛而言,这样也无关大雅),于是剩下的内容就只有下列步骤:

步骤 对应的操作
1. Reset init_ds18b20()
2 . 主机发送CC Write_DS18B20(0xCC); //跳过ROM
3. 主机发送44 H Write_DS18B20(0x44); //开始温度转换
4. 延时 Delay_OneWire(200); //模拟主机强制上拉总线
5 . Reset init_ds18b20()
6. 主机发送CC Write_DS18B20(0xCC); //跳过ROM
7. 主机发送BE Write_DS18B20(0xBE); //读取温度寄存器
8. 延时 Delay_OneWire(200); //模拟主机强制上拉总线
9. 读取温度 low = Read_DS18B20();high = Read_DS18B20();

对应这九步的代码如下:

/**
  * @brief  以带小数点的形式读取温度
  * @param  None
  * @retval temperature - float
  *
  */
float rd_temperature_f(void)
{
    unsigned int temp;
    float temperature;
    unsigned char low,high;
  
  	init_ds18b20();
  	Write_DS18B20(0xCC);		//跳过ROM
  	Write_DS18B20(0x44);		//开始温度转换
  	Delay_OneWire(200);			

  	init_ds18b20();
  	Write_DS18B20(0xCC);		//跳过ROM
  	Write_DS18B20(0xBE);		//读取温度寄存器

  	low = Read_DS18B20();
  	high = Read_DS18B20();
	
	temp = high;
	temp <<= 8;
	temp |= low;
	temperature = temp*0.0625;
  	return temperature;
}

除了以上步骤外还要修改onewire.c的延时函数,由于单总线协议没有时钟线,因此必须有准确的时间要求,而官方提供的是普通51单片机的代码,与IAP的时钟不一致,可以理解为IAP15的时钟比51单片机的时钟快12倍,也就是说源代码中的1us实际上在IAP15上运行只有1/12us,所以需要乘12倍。

/**
  * @brief  更改为之前12倍的延时
  * @param  None
  * @retval t - 代表延时的时长 单位:us
  * @author 
  */
void Delay_OneWire(unsigned int t)  //STC89C52RC ->IAP15
{
	unsigned char i;
	while(t--)
	{
		for(i=0;i<12;i++);
	}
}

关于DS18B20的代码需要修改的就这些了,对着数据手册还是可以很快写出来应用函数的。

DS1302

原理图

DS1302是常见的一款实时时钟芯片,它使用的是类三线SPI的通信协议,最标准的SPI是MISO(主收从发)、MOSI(主发从收)、SCL(时钟)、CS(片选),但在实际应用中多有改变,DS1302就是其中之一,跟上面提到的DS18B20一样,DS1302与主控之间也是主从关系,不接受主机命名从机是不能主动发送数据的,如果继续使用四线全双工的SPI显得有些浪费资源。因此会做出优化,改成只有三线的模式。其原理图如下:

SPI协议简介

这个部分官方已经给了驱动代码,赛场上不需要自己写,因为通信双方有统一的时钟线,所以不需要像单总线协议修改时间频率,想要了解参考此文——DS1302实时时钟原理和程序

赛场技巧

因为官方大大已经把初始化,读操作和写操作写好了,这里我们只要根据题目要求去调用即可。
首先还是来找出DS1302的芯片手册,找到第九页的最上面,我们的编程只需要基于这个表就行了。
第一列是读的寄存器,第二列是写的寄存器,这俩寄存器都是8位的,而后面就是寄存器对应的数据;
81h 和80h 对应着秒的十位(bit4-bit6)以及秒的个位(bit0-bit3);
剩下的以此类推即可。

在赛场上如果使用到此芯片,只需要自己按照上表编写时间写入以及读出的代码即可,由于底层已经给出,这里我们只需要按照如下步骤进行即可:
写入时间信息时:
1.关闭写保护,Write_Ds1302(0x8e,0);
2.写入对应单位的BCD码数据,写入格式如下:

Write_Ds1302(寄存器,对应数据的BCD码格式);		//以BCD码写入秒数据

3.开启写保护,Write_Ds1302(0x8e,0x80);
读取时间时:
直接将对应的寄存器数据赋值给自己定义的变量,格式如下:

miao=Read_Ds1302(0x81);

整个官方代码需要补充的如下:

/**
  * @brief  无需更改延时
  * @param  shi,fen,miao
  * @retval None
  * @author 
  */
void Set_Clock(char shi,char fen,char miao)
{
	Write_Ds1302(0x8e,0);							//关闭写保护
	Write_Ds1302(0x80,(miao/10)*16+miao%10);		//以BCD码写入秒数据
	Write_Ds1302(0x82,(fen/10)*16+fen%10);			//以BCD码写入分数据
	Write_Ds1302(0x84,(shi/10)*16+shi%10);			//以BCD码写入时数据
	Write_Ds1302(0x8e,0x80);						//打开写保护
}


//以下为读取数据
	miao=Read_Ds1302(0x81);
	fen=Read_Ds1302(0x83);
	shi=Read_Ds1302(0x85);

AT24C02

原理图

AT24C02是板载的EEPROM芯片,能够将保存的数据做到掉电不丢失,在比赛中主要用来记录一些关键数据,例如第九届的彩灯控制器,就用来存储ADC读取的值,除此之外还可能用来记录开机此次数之类的数据。板载AT24C02是 SOIC的封装,一共八个脚,其中SCL、SDA是IIC的通信引脚,A0-A3是设备的物理地址,当同一总线上挂载了多个此类型的芯片后利用这三个角来区分物理地址。比赛所用开发板的模拟IIC总线上挂载了AT24C02和PCF8591两个器件,在通信过程中需要通过地址来区别。

IIC协议简介

IIC的简介大家可以参考笔者另外一个系列的描述,树莓派4B学习笔记——IO通信篇(I2C),由于通信过程中的开启总线、停止总线、发送应答、等待应答、发送数据、接收数据这些底层官方提供的文档也已经给了。

详细介绍参照此文——【蓝桥杯单片机进阶强化-03】24C02存储器的基本原理与应用。

赛场技巧

1.找准器件地址

由于IIC的通信是需要地址确认身份的,所以要先弄明白器件地址咋来的;
老套路,先找到AT24C02的芯片手册,来到第十一页,找到这个图,02就是2k内存的意思,对照下图,可以知道该芯片的地址格式是:

1 0 1 0  A2 A1 A0 读写位

由上面的原理图可以推出A2 A1 A0都被硬件拉地,所以是0 0 0
然后就是根据读写操作来确定地址了。

读地址:1010 0001(0xA1)
写地址:1010 0000(0xA0)

2.写周期限制
在向AT24C02写入数据时,从发出命令到写完成,这个中间大致需要花费10ms的时间,如果再次期间重复发送就会导致数据错误,因此在官方IIC.c代码的基础上首先需要添加一个10ms延时函数,保证数据传输的稳定性。有关这个类软件延时,上一篇介绍过使用STC自动生成的方法。
生成代码如下:

void Delay10ms()		//@12.000MHz
{
	unsigned char i, j;

	i = 117;
	j = 184;
	do
	{
		while (--j);
	} while (--i);
}

3.根据读写时序进行写入和读出数据函数的编写
IIC的通信流程:开总线,写数据,等应答,关总线,其中第二步第三部可以重复写入不同的数据。
AT24C02写入流程:

(1)开启总线;(2)发送器件地址;(3)等待器件应答;
(4)发送要写入数据的地址;(5)等待应答;(6)发送写入的数据;
(7)等待应答;(8)关闭总线;(9)必要的延时;
一开二验三应答;
四地五答六数据;
七答八关九延时。
写入数据代码如下:

void Write_Eeprom(unsigned char add,unsigned char val)
{
    i2c_start();
    i2c_sendbyte(0xa0);//写
    i2c_waitack();
    i2c_sendbyte(add);
    i2c_waitack();
    i2c_sendbyte(val);
    i2c_waitack();
    i2c_stop();
	Delay10ms();
}

AT24C02读取流程:
读数据流程大致与写入相同,需要先写入要读取的数据地址add,然后写入读取的命令,最后按位接收数据代码如下:

unsigned char Read_Eeprom(unsigned char add)
{
	unsigned char da;
	i2c_start();
	i2c_sendbyte(0xa0);
	i2c_waitack();
	i2c_sendbyte(add);
	i2c_waitack();
	
	i2c_start();
	i2c_sendbyte(0xa1);
	i2c_waitack();
	da = i2c_receivebyte();
	i2c_sendack(1); 
	i2c_stop();
	
	return da;
}

然后就是在自己需要的位置加入对应的写入读取函数即可。
例:

	write_eeprom(1,(led_set_cnt[1]/100));  //向AT24C02地址0x00中写入数据
	delay();//延时10ms
	write_eeprom(2,(led_set_cnt[2]/100));
	delay();
	write_eeprom(3,(led_set_cnt[3]/100));
	delay();
	write_eeprom(4,(led_set_cnt[4]/100));

PCF8591

原理图

PCF8591是近几年省赛高频考点,它也是使用的IIC的协议,观察它也有A0、A1、A2的物理地址,也是全部接地,然后有AIN0—AIN3四个通道的AD采集口,还有一个AOUT的DA输出口。

赛场技巧

1.按照上面AT24C02的套路,我们首先还是要搞定器件地址,老规矩,打开芯片手册Page5,找到下图,前面四位是厂家设定的1001,后面四位和AT24C02类似,3位物理地址一位读写标志位。

根据上面AT24C02的经验,这里的读地址是:1001 0001(0x91)
写地址是:1001 0000 (0x90)
2.搞定地址后,还需要增加一点东西,由于这个芯片有4个AD通道,一个DA通道,所以在使用过程中需要进行额外的配置,来开启所需要的功能。这里我们直接看芯片手册的下一页Page6

PCF8591内部有一个八位寄存器用来使能这些功能。如上图所示:
第七位也就是最高位是固定的0;
第六位是使能D A的,如果需要使能就要将这个位置一,类似十二届的省赛就是用了此功能;
第五位以及第四位是配置采集信号的输入方式的,因为有一些传感器是使用的差分信号,要用ADC采集数据就需要有两个输入脚,此功能开发板蓝桥的用不上直接配置00即可。
然后第三位也是默认值0;
第二位是自动增量位(默认不配置写0);
第一位和第零位就是配置使能通道的,

使能通道
00 选中通道0
01 选中通道1
10 选中通道2
11 选中通道3

由于没有默认同时开启所有通道的标志位,所以在使用过程中只能需要那个就是使能那个,具体的使能通信流程和上面AT24C02的类似,举个栗子,开启ADC的通道3。代码如下:

void Init_Pcf8591_ADC3(void)
{
	i2c_start();//开启总线
	i2c_sendbyte(0x90);//写地址
	i2c_waitack();//等回应
	i2c_sendbyte(0x03);  //配置通道及功能
	i2c_waitack();//等待回应
	i2c_stop();//关闭总线
	operate_delay(10);//延时保证写入正确
}

赛场上根据题目需求改变通道及功能的寄存器就行。
4.读取ADC转换数据;流程也是惊人的相似,改一下地址即可。

unsigned char Adc_Pcf8591(void)
{
	unsigned char temp;

	i2c_start();
	i2c_sendbyte(0x91);
	i2c_waitack();
	temp = i2c_receivebyte();
	i2c_sendack(1);
	i2c_stop();
	
	return temp;
}

5.DA功能;十二届的省赛考到了DA功能,这里的配置其实和上面差不多,
根据第二步的推断,给第六位置一即可,也就是写入的功能和通道选择的寄存器配置成0100 0000(0x40);然后把对应电压的数据写入即可,之前看见有人在问这个电压变化怎么实现,看一眼芯片手册第八页就知道了。

需要写入一个八位的数据来控制输出电压,00-FF(0-255),DA会将基准电压分成256份,需要输出多大的电压,就用这个电压与基准电压比较,然后乘上255即可。代码如下:

void Init_Pcf8591_DAC(unsigned char Digital)
{
	i2c_start();
	i2c_sendbyte(0x90);
	i2c_waitack();
	i2c_sendbyte(0x40);
	i2c_waitack();
	i2c_sendbyte(Digital);
	i2c_waitack();
	i2c_stop();
}

例如基准电压Vref=5V,当Digital=153时,DAC输出电压为5*153/256=3V。

总结

比赛中常用板级协议的器件也是省赛最喜欢考的,往届一般都是SPI、IIC、单总线3选2组合外加数码管、按键一起考,目前省赛还没有涉及到UART,所以笔者也就没有去描述,有更高要求的同学可以自己去尝试。本文如有错误之处,欢迎大家指出。

目录

蓝桥杯单片机组——榨干选手资源包(STC)
蓝桥杯单片机组——榨干选手资源包(芯片数据手册)

蓝桥杯单片机组——程序框架及客观题
蓝桥杯CT107D单片机组第四至第九届省赛完整过程代码