一、简介
计划是通过STC12C5A60S2
单片机的ADC来采集霍尔传感器的电压值,并将其通过上文的2.4G Zigbee无线串口收发模块传送给树莓派。由于GPS的定位信息存在一定的误差,且项目中也不是特别需要,所以暂时不作为数据传输的对象。本文仅暂先介绍STC12C5A60S2
单片机的uart
串口通信。
二、前情提要
STC12C5A60S2接线说明:
STC12C5A60S2 ADC采集:
三、硬件准备
本次实验仅仅是测试了一下Uart收发,所以并无硬件上的改动。
四、软件准备
#include "main.h"
#include "LCD1602.h"
#include "GPS.h"
#include <intrins.h> //51基本运算(包括_nop_空函数)
//定义变量
unsigned char KEY_NUM = 0;
bit Page = 0;
unsigned char xdata Display_GPGGA_Buffer[68];
unsigned char xdata Display_GPRMC_Buffer[68];
bit Flag_OV = 0;
bit Flag_Calc_GPGGA_OK = 0;
bit Flag_Calc_GPRMC_OK = 0;
/*********************************************************************************************
*** ADC函数 ***
函数名:毫秒级CPU延时函数
调 用:DELAY_ADC_MS (?);
参 数:1~65535(参数不可为0)
返回值:无
结 果:占用CPU方式延时与参数数值相同的毫秒时间
备 注:应用于1T单片机时i<600,应用于12T单片机时i<125
/*********************************************************************************************/
void DELAY_ADC_MS (unsigned int a){
unsigned int i;
while( --a != 0){
for(i = 0; i < 600; i++);
}
}
/*********************************************************************************************
函数名:10位A/D转换初始化函数
调 用:Read_init (?);
参 数:输入的端口(0000 0XXX 其中XXX是设置输入端口号,可用十进制0~7表示,0表示P1.0,7表示P1.7)
返回值:无
结 果:开启ADC功能并设置ADC的输入端口
备 注:使用ADC功能时需要将对应的IO接口设置为高阻输入方式(例如:P1M1 = 0x01;)
/**********************************************************************************************/
void Read_init (unsigned char CHA){
unsigned char AD_FIN=0; //存储A/D转换标志
CHA &= 0x07; //选择ADC的8个接口中的一个(0000 0111 清0高5位)
ADC_CONTR = 0x40; //ADC转换的速度(0XX0 0000 其中XX控制速度,请根据数据手册设置)
_nop_();
ADC_CONTR |= CHA; //选择A/D当前通道
_nop_();
ADC_CONTR |= 0x80; //启动A/D电源
DELAY_ADC_MS(1); //使输入电压达到稳定(1ms即可)
}
/**********************************************************************************************/
/*********************************************************************************************
函数名:10位A/D转换函数
调 用:? = ADC_Read();
参 数:无
返回值:10位ADC数据高(从0到1023(十进制))
结 果:读出指定ADC接口的A/D转换值,并返回数值
备 注:适用于STC12C5A60S2系列单片机(必须使用STC12C5A60S2.h头文件)
/**********************************************************************************************/
unsigned int ADC_Read (void){
unsigned char AD_FIN=0; //存储A/D转换标志
ADC_CONTR |= 0x08; //启动A/D转换(0000 1000 令ADCS = 1)
_nop_();
_nop_();
_nop_();
_nop_();
while (AD_FIN ==0){ //等待A/D转换结束
AD_FIN = (ADC_CONTR & 0x10); //0001 0000测试A/D转换结束否
}
ADC_CONTR &= 0xE7; //1111 0111 清ADC_FLAG位, 关闭A/D转换,
return (ADC_RES*4+ADC_RESL);//返回A/D转换结果(10位ADC数据高8位在ADC_RES中,低2位在ADC_RESL中)
}
//****************************************************
//主函数
//****************************************************
void main()
{
unsigned int m;
//unsigned long str[5];
unsigned char i = 0;
Init_LCD1602();
LCD1602_write_com(0x80); //指针设置
//LCD1602_write_word("come to use!");
LCD1602_write_word("xxWelcome to use!");
Delay_ms(1000);
Uart_Init();
P1M1 = 0x01;//与2052正好是相反的设置
Read_init (0);
m = ADC_Read();
//str[0]=m/1000+48; //取千位 48 = 0x30
//str[1]=(m%1000)/100+48; //取百位
//str[2]=(m%100)/10+48; //取十位
//str[3]=m%10+48;
Delay_ms(1000);
LCD1602_write_com(0x80+0x40); //设置指针
LCD1602_write_word("AD:");
LCD1602_write_data(m/1000+0x30);
LCD1602_write_data((m%1000)/100+0x30);
LCD1602_write_data((m%100)/10+0x30);
LCD1602_write_data(m%10+0x30);
while(1)
{
Scan_Key();
m = ADC_Read();
//str[0]=m/1000+48; //取千位
//str[1]=(m%1000)/100+48; //取百位
//str[2]=(m%100)/10+48; //取十位
//str[3]=m%10+48;
if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'G' && RX_Buffer[6] == ',' && RX_Buffer[13] == '.') //确定是否收到"GPGGA"这一帧数据
{
for( i = 0; i < 68 ; i++)
{
Display_GPGGA_Buffer[i] = RX_Buffer[i];
}
Hour = (Display_GPGGA_Buffer[7]-0x30)*10+(Display_GPGGA_Buffer[8]-0x30)+8; //UTC时间转换到北京时间 UTC+8
//0x30为ASCII转换为数字
if( Hour >= 24) //溢出
{
Hour %= 24; //获取当前Hour
Flag_OV = 1; //日期进位
}
else
{
Flag_OV = 0;
}
Min_High = Display_GPGGA_Buffer[9];
Min_Low = Display_GPGGA_Buffer[10];
Sec_High = Display_GPGGA_Buffer[11];
Sec_Low = Display_GPGGA_Buffer[12];
Flag_Calc_GPGGA_OK = 1;
}
if(Page == 0 && Flag_Calc_GPGGA_OK == 1)
{
LED1 = ~LED1;
Flag_Calc_GPGGA_OK = 0;
LCD1602_write_com(0x80); //设置指针
LCD1602_write_data(Hour/10+0x30);
LCD1602_write_data(Hour%10+0x30);
LCD1602_write_data(':');
LCD1602_write_data(Min_High);
LCD1602_write_data(Min_Low);
LCD1602_write_data(':');
LCD1602_write_data(Sec_High);
LCD1602_write_data(Sec_Low);
LCD1602_write_word(" ");
LCD1602_write_data(Display_GPGGA_Buffer[54]);
LCD1602_write_data(Display_GPGGA_Buffer[55]);
LCD1602_write_data(Display_GPGGA_Buffer[56]);
LCD1602_write_data(Display_GPGGA_Buffer[57]);
LCD1602_write_word("m");
LCD1602_write_com(0x80+0x40); //设置指针
LCD1602_write_data(Display_GPGGA_Buffer[28]); //N 或者 S
LCD1602_write_data(Display_GPGGA_Buffer[17]); //纬度
LCD1602_write_data(Display_GPGGA_Buffer[18]); //纬度
LCD1602_write_data(0xdf); //度
LCD1602_write_data(Display_GPGGA_Buffer[19]); //纬度
LCD1602_write_data(Display_GPGGA_Buffer[20]); //纬度
LCD1602_write_word("'"); //秒
LCD1602_write_data(Display_GPGGA_Buffer[42]); //E 或者 W
LCD1602_write_data(Display_GPGGA_Buffer[30]); //经度
LCD1602_write_data(Display_GPGGA_Buffer[31]);
LCD1602_write_data(Display_GPGGA_Buffer[32]);
LCD1602_write_data(0xdf); //°
LCD1602_write_data(Display_GPGGA_Buffer[33]);
LCD1602_write_data(Display_GPGGA_Buffer[34]);
LCD1602_write_word("'");
}
if(Flag_GPS_OK == 1 && RX_Buffer[4] == 'M' && RX_Buffer[52] == ',' && RX_Buffer[59] == ',') //确定是否收到"GPRMC"这一帧数据
{
for( i = 0; i < 68 ; i++)
{
Display_GPRMC_Buffer[i] = RX_Buffer[i];
}
Year_High = Display_GPRMC_Buffer[57];
Year_Low = Display_GPRMC_Buffer[58];
Month_High = Display_GPRMC_Buffer[55];
Month_Low = Display_GPRMC_Buffer[56];
Day_High = Display_GPRMC_Buffer[53];
Day_Low = Display_GPRMC_Buffer[54];
if(Flag_OV == 1) //有进位
{
UTCDate2LocalDate(); //UTC日期转换为北京时间
}
Flag_Calc_GPRMC_OK = 1;
UartPrintf("INDEX:");
UartPrintASCII('"');
UartPrintf("1");
UartPrintASCII('"');
UartPrintf("\r\n");
UartPrintf("AD:");
UartPrintASCII('"');
UartPrintASCII(m/1000+0x30);
UartPrintASCII((m%1000)/100+0x30);
UartPrintASCII((m%100)/10+0x30);
UartPrintASCII(m%10+0x30);
UartPrintASCII('"');
UartPrintf("\r\n");
//Delay_ms(10000);
}
if(Page == 1 && Flag_Calc_GPRMC_OK == 1)
{
LED1 = ~LED1;
Flag_Calc_GPRMC_OK = 0;
LCD1602_write_com(0x80); //设置指针
LCD1602_write_word("20");
LCD1602_write_data(Year_High);
LCD1602_write_data(Year_Low);
LCD1602_write_data('-');
LCD1602_write_data(Month_High);
LCD1602_write_data(Month_Low);
LCD1602_write_data('-');
LCD1602_write_data(Day_High);
LCD1602_write_data(Day_Low);
LCD1602_write_data(' ');
LCD1602_write_data(m/1000+0x30);
LCD1602_write_data((m%1000)/100+0x30);
LCD1602_write_data((m%100)/10+0x30);
LCD1602_write_data(m%10+0x30);
LCD1602_write_com(0x80+0x40); //设置指针
LCD1602_write_word("Speed:"); //显示内容
LCD1602_write_data(Display_GPRMC_Buffer[46]);
LCD1602_write_data(Display_GPRMC_Buffer[47]);
LCD1602_write_data(Display_GPRMC_Buffer[48]);
LCD1602_write_data(Display_GPRMC_Buffer[49]);
LCD1602_write_data(Display_GPRMC_Buffer[50]);
LCD1602_write_word("m/s");
}
// 在此处发送数据会导致Page换页变得卡顿
/*UartPrintf("INDEX:");
UartPrintASCII('"');
UartPrintf("1");
UartPrintASCII('"');
UartPrintf("\r\n");
UartPrintf("AD:");
UartPrintASCII('"');
UartPrintASCII(m/1000+0x30);
UartPrintASCII((m%1000)/100+0x30);
UartPrintASCII((m%100)/10+0x30);
UartPrintASCII(m%10+0x30);
UartPrintASCII('"');
UartPrintf("\r\n");
Delay_ms(10000);*/
}
}
//****************************************************
//UTC日期与当地日期转换
//****************************************************
void UTCDate2LocalDate(void)
{
Day = (Day_High - 0x30) * 10 + (Day_Low-0x30) + 1; //日 加一
Month = (Month_High - 0x30) * 10 + (Month_Low - 0x30);
Year = 2000 + (Year_High - 0x30) * 10 + (Year_Low - 0x30);
MaxDay = GetMaxDay(Month,Year); //获取当月 天数 最大值
if(Day > MaxDay) //溢出
{
Day = 1;
Month += 1;
if(Month > 12)
{
Year+=1;
}
}
Day_High = Day/10 + 0x30; //转换日期值为ASCII
Day_Low = Day%10 + 0x30;
Month_High = Month/10 + 0x30; //转换月份值为ASCII
Month_Low = Month%10 + 0x30;
Year_High = Year%100/10 + 0x30; //转换年份值为ASCII
Year_Low = Year%10 + 0x30;
}
//****************************************************
//获取当月日期最大值
//****************************************************
unsigned char GetMaxDay(unsigned char Month_Value,unsigned int Year_Value)
{
unsigned char iDays;
switch(Month_Value)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
{
iDays = 31;
}
break;
case 2:
{
//2月份比较特殊,需要根据是不是闰年来判断当月是28天还29天
iDays = IsLeapYear(Year_Value)?29:28;
}
break;
case 4:
case 6:
case 9:
case 11:
{
iDays = 30;
}
break;
default : break;
}
return(iDays);
}
//****************************************************
//闰年检测
//****************************************************
bit IsLeapYear(unsigned int uiYear)
{
return (((uiYear%4)==0)&&((uiYear%100)!=0))||((uiYear%400)==0);
}
//****************************************************
//按键扫描程序
//****************************************************
void Scan_Key()
{
if( KEY4 == 0 ) //按键1扫描
{
Delay_ms(10); //延时去抖
if( KEY4 == 0 )
{
while(KEY4 == 0); //等待松手
KEY_NUM = 3;
Page = ~Page;
LCD1602_write_com(0X01); //清屏
}
}
}
//****************************************************
//MS延时函数(12M晶振下测试)
//****************************************************
void Delay_ms(unsigned int n)
{
unsigned int i,j;
for(i=0;i<n;i++)
for(j=0;j<123;j++);
}
发送指定索引和所采集到的ADC值到PC。
五、参考例程
#include <reg51.h>
typedef unsigned char uint8;
typedef unsigned int uint16;
uint8 Buf[]="hello world!\n";
void delay(uint16 n)
{
while (n--);
}
/*波特率为9600*/
void UART_init(void)
{
SCON = 0x50; //串口方式1
TMOD = 0x20; // 定时器使用方式2自动重载
TH1 = 0xFD; //9600波特率对应的预设数,定时器方式2下,TH1=TL1
TL1 = 0xFD;
TR1 = 1;//开启定时器,开始产生波特率
}
/*发送一个字符*/
void UART_send_byte(uint8 dat)
{
SBUF = dat; //把数据放到SBUF中
while (TI == 0);//未发送完毕就等待
TI = 0; //发送完毕后,要把TI重新置0
}
/*发送一个字符串*/
void UART_send_string(uint8 *buf)
{
while (*buf != '\0')
{
UART_send_byte(*buf++);
}
}
void main()
{
UART_init();
while (1)
{
UART_send_string(Buf);
delay(20000);
}
}