蓝桥杯单片机采集任务(考点总结)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文介绍一下蓝桥杯单片机采集任务考点,详细介绍了底层代码如何通过datasheet编写,对底层有疑问可以看底层代码讲解文章或b站up柳离风视频。

一、采集模块总结

1.ds18b20(中断里代码尽可能少,DS18B20时序太容易被打断了,中断里以后不要对unsigned long类型的变量%,用时很久)

没把握默写的同学一定要看手册写
程序总体框架
在这里插入图片描述
具体指令
1.跳过ROM
在这里插入图片描述
2.温度转换指令
在这里插入图片描述
3.温度读取指令
在这里插入图片描述
注意上电读的温度寄存器里的值是85
在这里插入图片描述
温度转换的最大时间是750ms
在这里插入图片描述
所以上电第一次读到的温度一定是85摄氏度

为了不影响参数判断,上电必须开始温度转换,并且到下次采集温度的时间间隔一定要至少750ms,如果上电第一个界面就显示温度的话,一定要先开启转换,delay_ms(750),再开EA,另外col_dly初始化设置的值大一点,保证第一次采集比显示快,这样显示的第一个温度为正常温度
如果上电第一个界面不是温度,就没必要delay_ms(750),但最开始还是需要转换一次。

//由于新版资源包底层已经时间扩大十二倍了,所以不需要再*12
//===================================================
//函数名称:uint rd_temperature(uchar dot)
//函数功能:读取ds18b20检测到的温度
//入口参数:dot (0:保留0位小数   1: 保留一位小数   2:保留两位小数)
//出口参数:temp (温度值)
//===================================================
unsigned int rd_temperature(uchar dot)
{
	uint temp;
	uchar tml,tmh;
	init_ds18b20();
	Write_DS18B20(0xcc);   //跳过ROM
	Write_DS18B20(0x44);   //温度转换
	
	init_ds18b20();
	Write_DS18B20(0xcc);  //跳过ROM
	Write_DS18B20(0xbe);  //温度读取
	
	tml = Read_DS18B20();  //先读第八位
	tmh = Read_DS18B20();  //再读高八位
	temp = (tmh << 8) |tml;
	switch(dot)
	{
		case 0 :   temp = temp * 0.0625;break;  //不保留小数
		case 1 :   temp = temp * 0.625;break;   //保留一位小数
		case 2 :   temp = temp * 6.25;break;    //保留两位小数
	}
	return temp;
}
void main()
{
		system_init();
		rd_temperature(1);
		delay_ms(750);
		EA = 1;
		while(1)
		{
			key_task();
			display_task();
			collect_task();
		}
}

2.ds1302

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

看手册把读写的地址写出来(头顶横杠代表低电平有效,否则高电平有效)
//如果想用十二小时制,需要注意第三行地址和数据,例如你要往进写上午八点,bit7就是1,bit5就是0,整体是10001000,对应BCD码是0x88,写进去就可以了。注意读的时候不能和之前一样,需要把bit7,bit5分别归零 ,再对16做除法和取余数。

code uchar write_addr[] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};   //写地址
code uchar read_addr[] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};    //读地址
uchar Time[] = {0x00,0x26,0x20,0x12,0x02,0x07,0x23};              //实时时间(用到几个写几个)
//===================================================
//函数名称:void ds1302_init()
//函数功能:初始化时间数据
//入口参数:无
//出口参数:无
//===================================================
void ds1302_init()
{
	uchar i;
	Write_Ds1302_Byte(0x8e,0x00);
	for(i = 0;i < 7 ;i++)  //这里的i也是用到几个写几个,否则时钟会乱跑
	{
		Write_Ds1302_Byte(write_addr[i],Time[i]);
	}
	Write_Ds1302_Byte(0x8e,0x80);
}
//===================================================
//函数名称:void ds1302_read()
//函数功能:读取时间数据
//入口参数:无
//出口参数:无
//===================================================
void ds1302_read()
{
	uchar i;
	for(i = 0;i < 7 ;i++)//这里的i也是用到几个写几个,否则时钟会乱跑
	{
		Time[i] = Read_Ds1302_Byte(read_addr[i]);
	}
}

//如果题目有时钟调节需求,需要变为下面的代码
//===================================================
//函数名称:void ds1302_init()
//函数功能:初始化时间数据
//入口参数:无
//出口参数:无
//===================================================
void ds1302_init()
{
	uchar i;
	Write_Ds1302_Byte(0x8e,0x00);
	for(i = 0;i < 3 ;i++)  //这里的i也是用到几个写几个,否则时钟会乱跑
	{
		Write_Ds1302_Byte(write_addr[i],Time[i]);
	}
	Write_Ds1302_Byte(0x8e,0x80);
}
//===================================================
//函数名称:void ds1302_read()
//函数功能:读取时间数据
//入口参数:无
//出口参数:无
//===================================================
void ds1302_read(unsigned char * temp)
{			
	unsigned char i;
	for(i = 0;i < 3;i++)
	{
		temp[i] = Read_Ds1302_Byte(read_addr[i]);
	}	
}
//===================================================
//函数名称:void Time_control(unsigned char * buf,unsigned char sign)
//函数功能:时间调节
//入口参数:buf(指针变量,传递数组首地址)     sign(0 : 加  1: 减) 
//出口参数:无
//===================================================
void Time_control(unsigned char * buf,unsigned char index,unsigned char sign)
{
	buf[index] = (buf[index]/16)*10 + buf[index]%16;
	if(sign == 0)
	{
		if(index == 2)
		{
			buf[index] = (buf[index] + 1)%24;
		}
		else 
		{
			buf[index] = (buf[index] + 1)%60;
		}
	}
	else 
	{
		if(index == 2)
		{
			if(buf[index] == 0)   buf[index] = 23;
			else 									buf[index] = buf[index] - 1;
		}
		else 
		{
			if(buf[index] == 0)   buf[index] = 59;
			else 									buf[index] = buf[index] - 1;
		}
	}		
  buf[index] = (buf[index]/10)*16 + buf[index]%10;
}

3.NE555(跳线帽记得把SIGNAL和P34接一起)

在这里插入图片描述

void Timer0Init_Count(void)		//计数模式
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x05;   //16位不自动重装
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计数
	//一定不要开中断即ET0 = 1;  切记 切记 切记
}
void timer1()  interrupt 3
{
	if(++count==1000)   //1秒算一次频率
	{
		fre=(TH0<<8)|TL0;
		TH0=TL0=0;
		count=0;
	}
     systick_ms++;
	  key_dly++;
	 display_dly++;
    collect_dly++;
	smg(SMG,dot,pos);
	led(LED,pos);
	if(++pos == 8) pos = 0;
	relay(state_relay);
}

4.PCF8591(测电压时用万用表,调到电压档,红表笔接D/A,黑表笔接GND)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里有个注意事项ADC上电初值是0x80,并且ADC读取的值是上次转换后的值,所以当多通道ADC读取时就会出现两个通道数据相反,解决办法就是每个通道各读两遍。

1.ADC
在这里插入图片描述

//===================================================
//函数名称:uchar PCF8591_Read(uchar control)
//函数功能:PCF8591 AD输入读取输入
//入口参数:control(控制字)
//出口参数:dat(数据)
//===================================================
uchar PCF8591_Read(uchar control)
{
	uchar dat;
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(control);
	IIC_WaitAck();
	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return dat;
}

2.DAC
在这里插入图片描述

//===================================================
//函数名称:void PCF8591_Write(uchar control,dat)
//函数功能:PCF8591 DA输出电压
//入口参数:control(控制字)     dat(数据)
//出口参数:无
//===================================================
void PCF8591_Write(uchar control,dat)
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(control);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}

5.超声波 (两个跳线帽都要接好)

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于比赛的时候可能会NE555和超声波一起考,我们将超声波使用PCA定时器来实现

#include "ultrasonic.h"
sbit TX = P1^0;  					// 发射引脚
sbit RX = P1^1;  					// 接收引脚

uchar read_distance(void)
{
  uchar distance,num = 10;
  TX = 0;
  CL = 0xF3;						// 设置定时初值
  CH = 0xFF;						// 设置定时初值
  CR = 1;							// 定时器0计时
  // TX引脚发送40KHz方波信号驱动超声波发送探头
  while(num--)
  {
    while(!CF);
    TX ^= 1;
		CL = 0xF3;						// 设置定时初值
		CH = 0xFF;						// 设置定时初值
    CF = 0;
  }
  CR = 0;
  CL = 0;							// 设置定时初值
  CH = 0;							// 设置定时初值
  CR = 1;
  while(RX && !CF);				// 等待收到脉冲
  CR = 0;
  if(CF)							// 发生溢出
  {
    CF = 0;
    distance = 255;
  }
  else							    // 计算距离
    distance = ((CH<<8)+CL)*0.017;
  return distance;
}

6.AT24C02

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

//===================================================
//函数名称:void AT24C02_Write(uchar addr,dat)
//函数功能:AT24C02字节写
//入口参数:addr(地址)      dat(数据)
//出口参数:无
//===================================================
void AT24C02_Write(uchar addr,dat)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}
//===================================================
//函数名称:void AT24C02_Write_Page(uchar * buf,uchar addr,uchar num)
//函数功能:AT24C02页写
//入口参数:buf(指针变量,传递数组的首地址)      addr(内存地址,必须是八的整数倍,可以写十进制)      num(数据个数)
//出口参数:无
//===================================================
void AT24C02_Write_Page(uchar * buf,uchar addr,uchar num)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	while(num--)
	{
		IIC_SendByte(*buf++);
		IIC_WaitAck();
	}
	IIC_Stop();
}

//===================================================
//函数名称:uchar AT24C02_Read(uchar addr)
//函数功能:AT24C02字节读
//入口参数:addr(地址)
//出口参数:dat(数据)
//===================================================
uchar AT24C02_Read(uchar addr)
{
	uchar dat;
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return dat;
}

//===================================================
//函数名称:void AT24C02_Read_Page(uchar * buf,uchar addr,uchar num)
//函数功能:AT24C02页读
//入口参数:buf(指针变量,传递数组的首地址)      addr(内存地址,必须是八的整数倍,可以写十进制)      num(数据个数)
//出口参数:无
//===================================================
void AT24C02_Read_Page(uchar * buf,uchar addr,uchar num)
{
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	while(num--)
	{
		*buf++ = IIC_RecByte();
		if(num)
		  IIC_SendAck(0);
		else
			IIC_SendAck(1);
	}
	IIC_Stop();
}

7.串口通信

在这里插入图片描述
我们这里用定时器2来做波特率发生器,这里暂时不要求手算波特率了
在这里插入图片描述
题目要求多少波特率设置多少就行,需要打开ES

uchar Uart_Rxindex;
uchar Uart_Rxbuf[10];
void UartInit(void)					//4800bps@12.000MHz
{ 
  SCON = 0x50;						// 8位数据,可变波特率
  AUXR |= 0x01;						// 串口1选择定时器2为波特率发生器
  AUXR |= 0x04;						// 定时器2时钟为Fosc, 即1T
  T2L = 0x8F;						// 设定定时初值
  T2H = 0xFD; 						// 设定定时初值
  AUXR |= 0x10;						// 启动定时器2
  ES = 1;									// 允许串口中断
}
void Uart0(void) interrupt 4
{
  if(RI)
  {
		if(Uart_Rxindex == 10)
			Uart_Rxindex = 0;
    Uart_Rxbuf[Uart_Rxindex++] = SBUF;
    RI = 0;
  }
}
char putchar(char ch)    //使用putchar重定向后,单片机就可以用printf串口输出数据了
{
    SBUF = ch;
    while (TI == 0);   // 等待发送完成
    TI = 0; // 发送完成标志位置0
    return ch;
}

在这里插入图片描述
在ISP打开串口助手,选好端口,设置好波特率
在这里插入图片描述

二、常见考点

1.采集量映射(只要是可以采集的数据都可能考)

映射分为两种:线性映射、非线性映射
1.线性映射(y = kx+b,先求斜率,带入坐标再求b)
第十三届国赛
在这里插入图片描述

//横坐标就直接用0-255数字量就行
//k = 100/255.0,因为单片机非float变量除法会自动舍弃小数,当然也可以按计算器把他算出来 k = 0.39216867.

//采集任务
void collect_task()
{
	uchar adc;
	if(collect_dly<500)return;
	collect_dly = 0;	
	adc = PCF8591_Read(0x03);
	hum = adc * (100/255.0);
}

第十四届省赛
在这里插入图片描述
//k = (90-10)/(2000-200) = 8 / 180.0,因为单片机非float变量除法会自动舍弃小数,当然也可以按计算器把他算出来 k = 0.044.
//b = 10 - (8/180.0) * 200 用计算器计算的结果为 b = 1.112.

hum = fre * 0.044 +1.112; 
if(hum<90&&hum>10)
	有效数据

2.非线性映射
第九届省赛
在这里插入图片描述

//采集任务
void collect_task()
{
	uchar adc;
	if(collect_dly<500)return;
	collect_dly = 0;	
	adc = PCF8591_Read(0x03);
	if(adc < 64)   level = 1;
	else if(adc >=64 && adc < 128)  level = 2;
	else if(adc >=128 && adc < 1192)  level = 3;
	else if(adc >=192 && adc < 256)  level = 4;
}

2.DA输出

第十三届国赛
在这里插入图片描述
//分段函数,这十几年DAC只有这一种考法,DAC与各种模拟量之间的函数关系

//采集任务
void collect_task()
{
	uchar adc;
	if(collect_dly<500)return;
	collect_dly = 0;	
	humidity=PCF8591_Read(0x43)/2.55;
	if(humidity==100) humidity=99;   //数码管只有两位
    if(humidity<humidity_set) dac=51;
	else if(humidity>80) dac=255;
	else dac=(204.0/(80-humidity_set))*humidity+(4080.0-255*humidity_set)/(80.0-humidity_set);
	PCF8591_Write(0x43,dac);
}

3.参数比较

在这里插入图片描述
//注意我们的电压因为要显示两位小数,所以放大了一百倍,参数要显示一位小数,放大十倍,电压要和参数做比较一定要到同一数量级
//比如现在参数要和电压做比较

	if((vol / 10.0  < vol_parmax) && (vol / 10.0  > vol_parmin))

在这里插入图片描述

	if(distance >= distance_set-5 && distance <= distance_set+5)
			alarm_count++;
		else
			alarm_count=0;
		if(alarm_count>2)
		{
			flag_alarm=1;
		}
		else
			flag_alarm=0;

在这里插入图片描述

		if(count_tri >= 2 && hum > hum_old && temp > temp_old)   //触发次数大于二且温湿度都升高
			L6_state = 1;
		else 
			L6_state = 0;

4.触发(明天更新)

1.触发方式
在这里插入图片描述

在这里插入图片描述

2.触发计数
在这里插入图片描述
在这里插入图片描述

3.触发记录数据

5.数据回显(明天更新)

在这里插入图片描述

6.最值、平均值

第十二届国赛
在这里插入图片描述
求平均值:每次采集的数据求和/count (如果题目要显示一位小数,那么每次采集的数据求和*10/count)
最大值:每次采到的数据和之前的最大值作比较,如果比最大值大就把新采到的数据赋给最大值
最小值:每次采到的数据和之前的最小值作比较,如果比最小值小就把新采到的数据赋给最小值

	distance_max = (distance>distance_max) ? distance : distance_max;
	distance_min = (distance<distance_min) ? distance :distance_min;	
	distance_sum = distance * 10 + distance_sum;   //在显示的时候除count,就可以保留一位小数

7.数据召测

第十届国赛
在这里插入图片描述

void uart_pro()
{
	if(index==0) return;
	if(uart_stick>=10)//  ʱ    
	{
		uart_stick=uart_flag=0;
	if(index>0)
	{
		if(index==4||index==6)
		{
		if(rec_buf[0]=='S'&&rec_buf[1]=='T'&&rec_buf[2]=='\r'&&rec_buf[3]=='\n')			
			printf("$%2d,%.2f\r\n",(uint)distance,temperature);		
		else if(rec_buf[0]=='P'&&rec_buf[1]=='A'&&rec_buf[2]=='R'&&rec_buf[3]=='A'&&rec_buf[4]=='\r'&&rec_buf[5]=='\n')
			printf("#%2d,%2d\r\n",(uint)distance_set,(uint)temperature_set);	
		else
			printf("ERROR\r\n");
	  }
		
		else
		printf("ERROR\r\n");	
		index=0;
	}
  }
}

更多资料关注B站UP柳离风
学习交流群
在这里插入图片描述

总结

以上就是蓝桥杯单片机采集任务的全部考点,希望大家全部掌握。

  • 41
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柳离风

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

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

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

打赏作者

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

抵扣说明:

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

余额充值