单片机课程设计——基于多终端控制的数据采集系统

1、题目

利用艾克姆科技 STC8A8K64D4 开发板套件和 STC 大学计划实验箱 STC8H8K64U 设计一个可由多终端控制的数据采集系统。 题目部分简称: A 板——艾克姆科技 STC8A8K64D4 开发板套件; B 板——STC 大学 计划实验箱 STC8H8K64U;三种模式——“2.2”节中的三种模式;三类按键——A 板矩阵 按键、B 板按键、遥控器按键;LCD——LCD12864。

 器件:两种开发板各一套, A 板套件中的 PAJ7620 手势识别传感器、遥控器、跳线帽 杜邦线等耗材。

2、要求

2.1 通信部分

A 板和 B 板通过串口的方式通信,遥控器与 B 板通过红外的方式通信,A 板与遥控器 不直接通信

2.2 数据采集

在 A 板制作三种模式以采集数据,A 板的按键可以随时控制三种模式的切换。模式内 容如下:

模式 1:PAJ7620 检测到的“目标物体亮度”数据绘制对应的曲线实时绘制在 LCD 上。

模式 2:PAJ7620 检测到的“体积”数据绘制对应的曲线实时绘制在 LCD 上。

模式 3:旋转 A 板电位器,在 LCD 上实时绘制获取到的数据对应的曲线。

三种模式中被采集的数据(亮度、体积、ADC 值)应实时显示在数码管上。

2.3 采集间隔部分

三种模式的采集时间间隔可变(以毫秒为单位),三类按键均可以调整三种模式的数据 采集时间间隔。时间间隔的不同通过曲线的绘制速率体现,并将时间间隔显示在数码管上。

2.4 报警部分

三类按键均可以设定报警阈值,阈值对应的直线绘制在 LCD 上,超过阈值则通过 B 板 的 LED 发出警报。

2.5 拓展部分

拓展功能至少包含一个本题目未包含的单片机知识点或器件。

3、设计与分析

3.1 外设结构图

3.2外设

艾克姆科技 STC8A8K64D4 开发板套件

STC 大学计划实验箱 STC8H8K64U

 PAJ7620 手势识别传感器

红外遥控器

其他外设皆集成在开发板上。

3.3 题目分析

  1. 根据题目,涉及到两套开发板的串口通信,A板作为主板,B板作为上位机主要下达两类指令——红外与按键;A板将超过阈值的信息传给B板,B板发出警报。
  2. 两类开发板均有涉及到数码管,所以会用到定时器中断显示数码管。
  3. 题目涉及到传感器数值与ADC电压值的读取,调用固定的函数。
  4. 读取的数值显示在数码管,在指定位的数码管显示变化的数值,动态显示(中断执行)。
  5. LCD12864绘制曲线,每读取一个数值绘制对应一个点,与前一个点连线。
  6. 采集间隔,使用延时函数。
  7. 阈值设定,主要是扫描指令,控制屏幕Y坐标。

3.4 I/O口设置

因为多数外设使用的是开发板上的集成好的外设,只需复制源代码即可,但其中A板上的LCD屏与板载的数码管引脚冲突,所以这里将数码管的引脚用杜邦线飞线到了其他没有占用的引脚

数码管:

sbit SEG_A0=P1^3;          //74HC138µÚ1Òý½Å
sbit SEG_A1=P1^4;          //74HC138µÚ2Òý½Å
sbit SEG_A2=P1^5;          //74HC138µÚ3Òý½Å
sbit SEG_DATA=P7^3;        //74HC595µÚ14Òý½Å
sbit SEG_SCK=P7^0;         //74HC595µÚ11Òý½Å
sbit SEG_LCLK=P1^2;        //74HC595µÚ12Òý½Å

串口通信采用的是UART2。Rx--------P10

                                            Tx--------P11

LED灯:

D1----------P52

D2----------P53

4、具体设计过程

4.1 模式选择

A板代码基于艾克姆科技 STC8A8K64D4 开发板官方提供的PAJ7620 手势识别传感器代码更改添加。原版代码将目标物体亮度与体积值放在了一个函数中,为方便分别调用亮度值与体积值,首先将亮度与体积分成两个函数。

uint8 get_ps_result(uint16 *p_brightness)
{
    uint8 temp;
	  temp = gs_read_byte(PAJ_OBJECT_BRIGHTNESS);//亮度
	
		*p_brightness = (uint16)temp;
		
		return GS_SUCCESS;
}

uint8 get_ps1_result(uint16 *p_size)
{
		uint8 read_buf[2];
		read_buf[0] = gs_read_byte(PAJ_OBJECT_SIZE_1);//大小
		read_buf[1] = gs_read_byte(PAJ_OBJECT_SIZE_2);
		*p_size = ((uint16)read_buf[1] & 0x0f)<<8 | read_buf[0];
		
		return GS_SUCCESS;
}

在主函数循环调用两个函数读取传感器数值

if(get_ps_result(&obj_brightness) == GS_SUCCESS)
{
    printf("obj_brightness: %d\r\n",obj_brightness);
	temp_x++;
}		

再调用ADC电压读取函数

float HandleADC1(void)
{
	uint16 Temp_signal1;
	float g_voltage;

	Temp_signal1=HandleADC();  
	
	if((Temp_signal1<TEMPMAX)&&(Temp_signal1>TEMPMIN))
	{
    g_voltage=(2.5*Temp_signal1)/4096;		 	  
	}		
	
	return g_voltage;	
}

按照题目要求用按键控制三种模式,在主循环中对按键进行条件判断

	button_num = Keys_Scan(0);               
    if(button_num == KEY1_ON)                
    {
      
	    ps_init(); 
			leds_off(); 
			printf("PS test started\r\n");
			while(1)
			{
				if(get_ps_result(&obj_brightness) == GS_SUCCESS)
				{
					printf("obj_brightness: %d\r\n",obj_brightness);
				}
				ss[0]=obj_brightness/100;ss[1]=(obj_brightness%100)/10;ss[2]=obj_brightness%10;
				delay_ms(80);                
				led_toggle(LED_1);                
				button_num = Keys_Scan(0); 
				if((button_num == KEY2_ON) || (button_num == KEY3_ON))break;  
			}
    }

4.2 数码管显示获取的数值

题目要求将获取到数值显示在数码管上,其中亮度与体积最大值分别是255和900,ADC获取的电压值最大是2.5V,也就是说获取到数值最多有三位数,由于数码管一次中断只能显示一位数,所以要将获取到的数值按位拆分成三份,存入数组。

ss[0]=obj_brightness/100;ss[1]=(obj_brightness%100)/10;ss[2]=obj_brightness%10;

由于ADC电压值只有两位且带有小数点,带有小数点的数码管的“段选”值需要单独设置,所以,在选择模式三(ADC)时要设置一个全局变量作为标志位,在中断中判断该标志位,当中断已知是模式三时,第一位数的段选值要带有小数点。

		if(button_num == KEY3_ON)                
	  {
			ps_init(); 
			leds_off();     
			printf("ADC\r\n");
			while(1)
			{
				adc_flag=1;//模式三的标志位
				printf("\r\n ADC¶Ë¿Úµçѹֵ£º %.1f V\r\n",HandleADC1());     
				adc_num=HandleADC1();
				integer_part=(int)adc_num;
				decimal_part=(int)((adc_num-integer_part)*10);
				ss[0]=integer_part;ss[1]=decimal_part;ss[2]=0;
				delay_ms(80); //Èí¼þÑÓʱ100ms
				led_toggle(LED_3);             
				button_num = Keys_Scan(0); 
				if((button_num == KEY1_ON) || (button_num == KEY2_ON))
				{
					adc_flag=0;//跳出模式三,标志位清空
					break;  
				}
			}
    }

定时器中断服务函数

void Timer3_Isr(void) interrupt 19
{
	 cnt++;                  //2ms进入一次中断
	if(cnt == 1)             //2msË¢ÐÂÏÔʾÊýÂë¹Ü1λµÄÐÅÏ¢
	{
		cnt = 0;
		if(adc_flag==0)      
		{
			if(xyz==2)
				seg_duan=duanxuan_point(ss[xyz]);
			else
				seg_duan=duanxuan(ss[xyz]);
		}
			
		else                 //显示ADC
		{
			if(xyz==0)
				seg_duan=duanxuan_point(ss[xyz]);//带小数点
			else
				seg_duan=duanxuan(ss[xyz]);//不带小数点
		}
		
		SEG_Write_Data(seg_duan,seg_wei);              //显示
		SEG_Refresh();                                 //刷新
		if(seg_wei>4)
			seg_wei--;
		else
			seg_wei=7;
		if(xyz<3)
			xyz++;
		else
			xyz=0;		
	}	
	
}

两种数码管段选函数

uint8 duanxuan(uint8 iii)//不带小数点
{
	xuanduan=0;
			switch(iii)
			{
				case 0:  xuanduan = seg_num[0]; break;
				case 9:  xuanduan = seg_num[9]; break;
				case 8:  xuanduan = seg_num[8]; break;    
				case 7:  xuanduan = seg_num[7]; break;     
				case 6:  xuanduan = seg_num[6]; break;    
				case 5:  xuanduan = seg_num[5]; break;   
				case 4:  xuanduan = seg_num[4]; break;    
				case 3:  xuanduan = seg_num[3]; break;   
				case 2:  xuanduan = seg_num[2]; break;     
				case 1:  xuanduan = seg_num[1]; break;    
			}
	return xuanduan;
}

uint8 duanxuan_point(uint8 kkk)//带小数点
{
	xuanduan=0;
			switch(kkk)
			{
				case 0:  xuanduan = seg_num[0]| 0x01; break;
				case 9:  xuanduan = seg_num[9]| 0x01; break;
				case 8:  xuanduan = seg_num[8]| 0x01; break;     
				case 7:  xuanduan = seg_num[7]| 0x01; break;     
				case 6:  xuanduan = seg_num[6]| 0x01; break;     
				case 5:  xuanduan = seg_num[5]| 0x01; break;    
				case 4:  xuanduan = seg_num[4]| 0x01; break;     
				case 3:  xuanduan = seg_num[3]| 0x01; break;  
				case 2:  xuanduan = seg_num[2]| 0x01; break;    
				case 1:  xuanduan = seg_num[1]| 0x01; break;   
			}
	return xuanduan;
}

4.3 采样速度数码管显示

题目要求将采样时间间隔显示在数码管上,显示传感器数值占用前三位数码管(7-5位),用第八位数码管(第0位)显示采样间隔。在矩阵键盘文件内设置全局变量(show_isr_speed),将对应按键下的时间间隔赋值给该变量,在中断服务函数中调用该变量;判断数码管是否显示了前三位(7-5位),显示前三位后“位选”值为0,显示第零位,在显示第0为数码管时,有关前三位数码管的例程通过条件判断不运行

void Timer3_Isr(void) interrupt 19
{
	 cnt++;                 
	if(cnt == 1)             
	{			
		cnt = 0;
		if(seg_wei==0)//第0位数码管显示采样速率
		{
			show_speed=show_isr_speed;
			seg_duan=duanxuan(show_speed);
		}
		if(seg_wei != 0)//显示传感器数值
		{
			if(adc_flag==0)
			{
				if(xyz==2)
					seg_duan=duanxuan_point(ss[xyz]);
				else
					seg_duan=duanxuan(ss[xyz]);
			}			
			else
			{
				if(xyz==0)
					seg_duan=duanxuan_point(ss[xyz]);
				else
					seg_duan=duanxuan(ss[xyz]);
			}			
		}
		SEG_Write_Data(seg_duan,seg_wei);             
		SEG_Refresh();                                
		if(seg_wei>4)
			seg_wei--;	
		else if(seg_wei==4)
			seg_wei=0;
		else
			seg_wei=7;
		if(seg_wei != 0)//显示采样率时不执行数组
		{
			if(xyz<3)
				xyz++;
			else
				xyz=0;
		}		
	}	

4.4 LCD绘制波形

在LCD屏上画数据波形,为了使波形连续且平滑,主函数每读取一次传感器数值X轴加1,Y轴坐标与前一次Y坐标相连。当曲线到屏幕边沿时(127)刷屏。

绘制波形函数

void Port_DrawCurve(uint8 x,uint16 value)
{
	static uint8 last_x,last_y;
	uint16 temp_y=0;
	temp_y=value/5;
		
	if(x==0)
	{
		LCD_DrawDot(x,temp_y,1);
		last_x=0;
		last_y=temp_y;
	}
	else
	{
		LCD_DrawLine(last_x,last_y,x,temp_y);
		last_x=x;
		last_y=temp_y;
	}
}

主函数

					if(get_ps_result(&obj_brightness) == GS_SUCCESS)
					{
						printf("obj_brightness: %d\r\n",obj_brightness);
						temp_x++;
					}		
				if(temp_x>=128)
				{
					temp_x=0;
					Fill_GDRAM(0x00);  //ÇåÆÁ
					DrawHline(1,127,1,1);
					DrawVline(1,0,63,1);
					DrawHline(0,127,yu,1);
				}
				Port_DrawCurve(temp_x,obj_brightness);

4.5 矩阵按键

四个按键分别对应三种模式和清屏暂停功能,设置采样间隔和阈值设定需要使用矩阵键盘。第一排(S3-S6)对应第一采样速度到第四采样速度;第二排(S7-S10)对应设置阈值、阈值加、阈值减、退出阈值设置。

采样间隔主要使用不同时间间隔的延时函数,以达到控制采样速率的效果。

获取调速键值

uint8 key_speed(void)
{
	  static uint8 last_speed;
		uint8 temp1=0;
		uint8 now_speed;	
		now_speed=KeyScan();
		if(now_speed==1)
		{
			temp1=0;
			last_speed=temp1;
		}
		else if(now_speed==2)
		{
			temp1=2;
			last_speed=temp1;
		}
		else if(now_speed==3)
		{
			temp1=4;
			last_speed=temp1;
		}
		else if(now_speed==4)
		{
			temp1=6;
			last_speed=temp1;
		}
		else temp1=last_speed;
		
		return temp1;
}

主函数调用

temp=key_speed();		
if(temp != 0)
{
    switch(temp)
	    {
		    case 2:
				delay_ms(50);break;
			case 4:
				delay_ms(100);break;
			case 6:
				delay_ms(200);break;
		}
}

设定阈值的模式就是将阈值加与阈值减放进一个while循环中,判断条件就是是否按下阈值设定的按键,按下后便判断是加阈值还是减阈值,当按下退出设置后跳出循环。退出时要清屏,将之前的阈值清掉。

uint8 key_top(void)
{
	  static uint8 last_top;
		uint8 now_top;	
		now_top=KeyScan();
		if(now_top==5)
		{
			top_flag=1;
		}
		if(now_top==8)
		{
			top_flag=0;
		}
		if(top_flag==1)
		{		
			if(now_top==6)
			{
				top+=10;
				last_top=top;
			}
			else if(now_top==7)
			{
				top-=10;
				last_top=top;
			}
			else top=last_top;
		}
		return top;
}

主函数调用

				key_yu=key_top();
				while(key_yu==1)
				{
					key_yu=key_top();
					if(key_yu==0)
					{
						Fill_GDRAM(0x00);  //ÇåÆÁ
						DrawHline(1,127,1,1);
						DrawVline(1,0,63,1);
						break;
					}					
					if((KeyScan()==6)||(HW_module==14))
					{
						yu+=5;
					}
					if((KeyScan()==7)||(HW_module==15))
					{
						yu-=5;
					}
					DrawHline(0,127,yu,1);
				}

4.6 B板按键

A板的功能几乎已经实现。

B板的代码是基于红外遥控接收程序(NEC码)进行的修改与增加。B板采用定时器2进行串口通信。红外遥控编码的接收通过定时器0中断实现。主函数循环中一直读取接收数值,设置按键“0”“100+”“200+”“EQ”分别实现三种模式与刷屏暂停的功能;按键“1”“2”“3”“4”分别对应四种采样间隔;“⏪”“⏩”“+”“-”分别对应“设置阈值”“退出阈值”“增大阈值”“减小阈值”功能。

当接收到遥控指令后,将指令赋值给定时器的SBUF寄存器,通过定时器2的中断服务函数发送出去。

            if(B_IR_Press)      //红外识别到按键
            {
                B_IR_Press = 0;
                
                LED8[0] = (u8)((UserCode >> 12) & 0x0f);    //Óû§Âë¸ß×ֽڵĸ߰ë×Ö½Ú
                LED8[1] = (u8)((UserCode >> 8)  & 0x0f);    //Óû§Âë¸ß×ֽڵĵͰë×Ö½Ú
                LED8[2] = (u8)((UserCode >> 4)  & 0x0f);    //Óû§ÂëµÍ×ֽڵĸ߰ë×Ö½Ú
                LED8[3] = (u8)(UserCode & 0x0f);            //Óû§ÂëµÍ×ֽڵĵͰë×Ö½Ú
                LED8[6] = IR_code >> 4;
                LED8[7] = IR_code & 0x0f;

							  S2BUF = IR_code;//将红外指令发送给A
								B_TX2_Busy = 1;
								while(B_TX2_Busy);
            }

A板通过定时器2实现串口通信功能,接收到B板发送的遥控按键对应的十六进制数值,设置一个全局变量,通过给全局变量赋不同的值代表不同的按键指令。(在使用完红外遥控后再使用A板按键需要先按遥控的“CH-”使红外指令处于空指令状态,A板按键才能有效)

void UART2_Tx_Puts(void)
{
  if(Flag2)      //接收到数据
	{
		if(uart2temp==0x45)//空指令
		{
			HW_module=0;
		}

		if((uart2temp==0x16)||(uart2temp==0xC1))//MODULE
		{
			HW_module=1;
		}
		else if((uart2temp==0x19)||(uart2temp==0xC2))
		{
			HW_module=2;
		}
		else if((uart2temp==0x0d)||(uart2temp==0xC3))
		{
			HW_module=3;
		}
		else if((uart2temp==0x09)||(uart2temp==0xC4))
		{
			HW_module=4;
		}
		else if((uart2temp==0x0c)||(uart2temp==0xA1))//SPEED
		{
			HW_module=5;
		}
		else if((uart2temp==0x18)||(uart2temp==0xA2))
		{
			HW_module=6;
		}
		else if((uart2temp==0x5e)||(uart2temp==0xA3))
		{
			HW_module=7;
		}
		else if((uart2temp==0x08)||(uart2temp==0xA4))
		{
			HW_module=8;
		}
		else if((uart2temp==0xB1)||(uart2temp==0x44))//阈值
		{
			HW_module=13;
		}
		else if((uart2temp==0xB2)||(uart2temp==0x15))
		{
			HW_module=14;
		}
		else if((uart2temp==0xB3)||(uart2temp==0x07))
		{
			HW_module=15;
		}
		else if((uart2temp==0xB4)||(uart2temp==0x40))
		{
			HW_module=16;
		}

		Flag2=FALSE;                          
  }
}

由于B板矩阵按键较少,所以B板按键采用ADC按键,每个按键设置键值,当主循环中条件判断到固定的键值时,就发送设定好的十六进制编码,A板接收。ADC按键设置了12个,功能与红外遥控相同。

            if(KeyCode > 0)     //检测到按键
            {
								if(KeyCode == 1)   //模式
                {
									S2BUF = 0xC1;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
								if(KeyCode == 2)   //hour +1
                {
									S2BUF = 0xC2;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
								if(KeyCode == 3)   //hour +1
                {
									S2BUF = 0xC3;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
								if(KeyCode == 4)   //hour +1
                {
									S2BUF = 0xC4;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }

                if(KeyCode == 5)   //采样间隔
                {
									S2BUF = 0xA1;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 6)   //hour -1
                {
                  S2BUF = 0xA2;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 7)   //minute +1
                {
                  S2BUF = 0xA3;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 8)   //minute -1
                {
                  S2BUF = 0xA4;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
								if(KeyCode == 9)   //阈值
                {
									S2BUF = 0xB1;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 10)   //hour -1
                {
                  S2BUF = 0xB2;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 11)   //minute +1
                {
                  S2BUF = 0xB3;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 12)   //minute -1
                {
                  S2BUF = 0xB4;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }
                if(KeyCode == 16)   //空指令
                {
                  S2BUF = 0xF0;
									B_TX2_Busy = 1;
									while(B_TX2_Busy);
                }

                KeyCode = 0;
            }

当A板传感器数值超过阈值时通过串口发送个B板一个特定的十六进制指令同时A板上的LED4闪烁警报,B板收到指令后通过蜂鸣器发出警报。

A板

				if((obj_brightness/5)>yu)
				{
					led_toggle(LED_4);
					SendDataByUart2(0x01);
				}
				else
				{
					led_off(LED_4);
					SendDataByUart2(0x02);
				}

B板

        if((TX2_Cnt != RX2_Cnt)&& (!B_TX2_Busy))   //接收到数据
        {
					if(RX2_Buffer[TX2_Cnt]==0x01)
					{
						BEEP=0;
                        delay_ms(250);
                        BEEP=1;
						delay_ms(250);
					}
					else if(RX2_Buffer[TX2_Cnt]==0x02)
					{
						BEEP=1;	
					}
          if(++TX2_Cnt >= UART2_BUF_LENGTH)   TX2_Cnt = 0;
        }

5、代码

本项目大致思路如上,经调试几乎可以达到题目要求效果,有些步骤可能过于繁琐或表述不清,还请批评指正。

下附两套代码,一套是A、B板完整的代码,一套是删减版,删减版包含功能:获取传感器数据、三种模式切换(A版基础按键和红外)、A板矩阵按键调速、B板报警(阈值不可调)

链接:https://pan.baidu.com/s/1zfW9rdHGPwiOEiP52g3CFw?pwd=fw9r 
提取码:fw9r

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值