📄 ussensor.c
字号:
/***************************************************/
/* 超声波测距传感器 */
/* —— 程序 */
/* 20080224 */
/* By DingQi */
/***************************************************/
// 注:以下文档的 TAB 为 2 个字符!
/*------------------------------------------------------------------------
软件要完成的功能为:
a) 通过串口接收命令、返回测量
b) 超声波脉冲发射;
c) 超声波检测和计时;
d) 计算测量结果
e) 根据工作模式返回数据
程序主要有四部分:
1) 通讯的建立和处理
2) 超声波脉冲发射;
3) 超声波的检测和计时处理
4) 数据存放和返回
硬件资源分配:
P10 - P13 —— 控制TL852增益
P14 - P17 —— 保留
P30(RXD)、P31(TXD) —— UART通讯或者软件模拟I2C
P32(INT0) —— 接SOUT端,作为超声波发射时的852输出抑制,置为OC输出。
P33(INT1) —— 超声波接收输入
P34、P35 —— 产生超声波发射方波
P37 —— 工作指示灯
INT0 —— 保留
INT1 —— 作为收到超声波回波中断
Timer0 —— 产生 1ms 时基
Timer1 —— UART波特率发生器
PCA0 —— 保留
PCA1 —— 发射时作为 40 KHz方波发生计时,接收时作为增益变换计时,采用同样的工作模式
UART —— 串行通讯,命令输入和数据返回
通讯协议及命令定义
基本通讯格式:(和小车及无线接口兼容)
标准UART格式 —— 19200 8 N 1
帧格式:
帧头(2字节) 接收方地址(1字节) 发送方地址(1字节) 帧长(1字节)
命令(1字节) 数据域(N字节) 校验和(1字节)
其中:
帧头 —— 由2个特殊的字节 0x55 0xAA构成;
接收方地址 —— 通讯对象的“名字”,在有线通讯时也许多余,但无线时就需要了。
发送方地址 —— 告诉接收方,便于接收方回答。
帧长 —— 命令和数据域字节之和,
命令 —— 说明操作内容,详见下面的定义
数据域 —— 与命令配合,表达一个完整的含义。
校验和 —— 从命令开始到数据域结束所有字节的算术和,取最低字节的反码。
具体的命令定义:
为了便于调试,保留小车中设计的读写内存命令。
命令一 :读内存,实现读指定地址开始的 N 个字节,地址用两字节表示。
命令字 —— 0x01
数据域 —— 低地址(1字节) 高地址(1字节) 读字节数(1字节)
地址与硬件的对应关系:
0x0000 — 0x00FF —— 对应STC12LE2052的256字节内部RAM(idata);
0x0100 — 0x7FFF —— 保留,为大RAM的单片机预留;
0x7F80 — 0x7FFF —— 对应STC12LE2052的128字节SFR;
0x8000 — 0x87FF —— 对应STC12LE2052的2K FlashROM(Code);
0x8700 — 0xFFFF —— 保留,为大ROM的单片机预留;
例:要读地址 0x56起始的3字节内部RAM数据,命令帧如下:
0x55 0xAA XX XX 0x04 0x01 0x56 0x00 0x03 0xA5
返回数据帧为:
帧头 发送方地址 自己的地址 帧长 命令 低地址 高地址 读字节数
N字节数据 校验和
返回帧中将命令及附属信息(地址、读字节数 )包含在内,虽然有些冗余,但保证了信息的完备性,
不需要接收时还要查找原来读的是什么, 为通讯需求日渐复杂提供方便。
命令二:写内存,实现写指定地址开始的 N 个字节,地址用两字节表示。
命令字 —— 0x02
数据域 —— 低地址(1字节) 高地址(1字节) 写字节数(1字节)数据(N字节)
其地址与硬件的关系与读命令相同。
返回数据帧为:
帧头 发送方地址 自己的地址 帧长 命令 低地址 高地址 写成功字节数 校验和
通过“写成功字节数”来告之发送方是否写成功,如果为“0”,表示写操作失败。
命令三:设置工作模式,设置测量模式,并启动测量。
命令字 —— 0x03
数据域 —— 工作模式(1字节) 工作参数(1字节)
其中:
工作模式 —— 高4位为主模式,低4位为子模式;
工作参数 —— 与工作模式对应,自动测量时为测量周期,单位10ms;单次测量时为测量的次数,暂定最多为8次。
返回数据帧为:
帧头 发送方地址 自己的地址 帧长 命令 测量数据 校验和
对于自动模式,测量数据返回一个字节“0”,说明OK。
对于单次测量模式,测量数据为工作模式中所要求的数据。
自动模式对应的子模式:
a) 自动数据返回 —— 每测完一个数据都返回,命令位置返回工作模式;
b) 被动数据返回 —— 内部只是测量、保存数据,等待读数据命令读回,读数据命令实际上只对自动模式的被动数据返回有效。
单次测量对应的子模式:
a) 无数据处理返回 —— 只是将命令中要求测量的数据全部返回
b) 剔除最大最小值返回 —— 将测量数据中最大、最小值剔除,返回数据比指定数少2个,当指定的测量次数小于 3 时,
强制切换到“无数据处理模式”;
c) 平均值返回 —— 将指定的测量数据平均后返回,只有一个平均值;
d) 剔除最大最小后平均值返回 —— 先剔除最大最小值,剩下的再取平均值,当数据数小于 4时,
强制切换到“剔除最大最小值” 模式。
上述返回帧中命令位置返回工作模式。
命令四:读测量数据,对于自动测量方式的被动数据返回模式,需要此命令。
命令字 —— 0x04
数据域 —— 读最近几次的数据(1字节)
返回数据帧为:
帧头 发送方地址 自己的地址 帧长 命令 测量数据 校验和
------------------------------------------------------------------------*/
#pragma PR
#pragma OT(5,size)
#pragma Listinclude
#pragma code
#include <STC12C5410AD.h> /* STC12C5410AD 的头文件*/
#include <Port.H>
#include <Constant.H>
#include <Var.H>
void init_hardware(void); // 硬件初始化函数
void init_var(void); // 初始化变量
void feed_watchdog(void); // 清看门狗计数器
void init_SIO(unsigned char baud);
bit dataFrame_OK(void);
void do_Command(void);
void startUltraSonicSend(void);
void sortLastData(unsigned char ucDataNum);
void backMeaData(unsigned char ucMode);
/******** 主程序 *************/
void main(void)
{
unsigned long ulMeaTime;
unsigned char sum;
union
{
unsigned int all;
unsigned char b[2];
}uitemp;
init_hardware();
init_var();
EA = TRUE; // 启动中断,开始正常工作
while(1)
{
/*-------- 时基处理 --------*/
if(g_b1msFlag)
{
g_b1msFlag =FALSE;
gc_uc10msCnt --;
if(gc_uc10msCnt == 0)
{
gc_uc10msCnt = 10;
// 10ms 为计时单位的处理
if(gc_ucMeaPeriodCnt>0)
{
gc_ucMeaPeriodCnt--;
}
}
if(gc_ucGapAtSingleMea >0)
{
gc_ucGapAtSingleMea--;
if(gc_ucGapAtSingleMea == 0)
{
startUltraSonicSend(); // 启动超声波发射, 开始下一次数据采集
}
}
/* ------ 工作指示处理 -------*/
gc_uiWorkDispTimeCnt--;
if(gc_uiWorkDispTimeCnt == 0)
{
g_bWorkDisp = ~g_bWorkDisp;
gc_ucWorkDispNumCnt --;
if(gc_ucWorkDispNumCnt == 0)
{
//恢复到正常显示
g_ucWorkDispStat = NORMAL;
gc_ucWorkDispNumCnt = ga_ucWorkDispNum[g_ucWorkDispStat];
}
gc_uiWorkDispTimeCnt = ga_uiWorkDispTime[g_ucWorkDispStat];
}
gc_uiWdtCnt--; // WDT 处理
if(gc_uiWdtCnt == 0)
{
gc_uiWdtCnt = WDTTIME_C;
feed_watchdog();
}
}
/* -------- 通讯处理 ------ */
if(g_bNewData)
{
g_bNewData = FALSE;
if(dataFrame_OK()) // 串口收到数据后的处理,与PC程序中的函数 DataFrame_OK() 相当
{
g_ucWorkDispStat = UART_OK; // 指示收到命令
gc_ucWorkDispNumCnt = ga_ucWorkDispNum[g_ucWorkDispStat];
gc_uiWorkDispTimeCnt = ga_uiWorkDispTime[g_ucWorkDispStat];
do_Command(); // 执行数据帧中的命令
}
}
/* -------- 工作状态处理 -------- */
switch(g_ucWorkStat)
{
case WAIT_START:
{
// 在等待状态则检测工作模式
switch(g_ucWorkMode&GET_MAIN_MODE)
{
case STOP_MEASURE:
{
CL = 0;
CH = 0;
CCON = CCON&STOPPCA_C; // 停止 PCA
g_bSend_Ctrl = SEND_INVALID; // 超声波输出驱动无效
g_bUltraSonicDrvValid = FALSE;
break; // 停止测量模式
}
case AUTO_MEASURE:
{
// 自动测量模式处理
if(gc_ucMeaPeriodCnt == 0)
{
gc_ucMeaPeriodCnt = g_ucModePara; // 测量周期计数初始化
startUltraSonicSend(); // 启动超声波发射
}
break;
}
case SINGLE_MEASURE:
{
g_ucModePara = g_ucModePara &(DATA_SAVE_NUM-1); // 取测量次数,并做限制处理
if(g_ucModePara == 0)
{
g_ucModePara = 1; // 强制测量一次
}
gc_ucMeaTimesCnt = g_ucModePara;
startUltraSonicSend(); // 启动超声波发射
break;
}
default: break;
}
break;
}
case ULTRASONIC_T:
{
break; // 超声波发射处理在中断中完成
}
case ULTRASONIC_R:
{
if(g_bGainCtrl)
{
g_bGainCtrl = FALSE;
if(gc_ucGainCtrl_No <11)
{
g_ui852GainCtrlTime.all += ga_uiDeltaTime[gc_ucGainCtrl_No];
CCAP1L = g_ui852GainCtrlTime.b[1]; // 加载下一次比较值
CCAP1H = g_ui852GainCtrlTime.b[0];
g_uc852GainCtrl = (g_uc852GainCtrl&0xF0)|gc_ucGainCtrl_No; // 输出增益控制
if(gc_ucGainCtrl_No == 0)
{
g_bCut_Off = CUT_OFF_VALID; // 启动回波抑制
}
gc_ucGainCtrl_No++;
}
else
{
if(gc_ucPCA_OverCnt<11)
{
// 如果计时小于 60 ms,则维持最大增益,等待
CCAP1L = 10; // 加载下一次比较值
CCAP1H = 0;
g_uc852GainCtrl = 11; // 最大增益
}
else
{
g_uc852GainCtrl = g_uc852GainCtrl&0xF0; // 如果到了60ms,将增益回零
CCON = CCON&STOPPCA_C; // 停止 PCA 计时器,结束测量,因为超过了10米距离时间
g_uiStopTime.all = 0;
gc_ucPCA_OverCnt = 65535; // 设置计时溢出标志
g_ucWorkStat = CAL_RESULT; // 转换到计算状态
g_b852Inhibit = DIS852OUT; // 禁止852输出
}
}
}
break;
}
case CAL_RESULT:
{
if(gc_ucPCA_OverCnt!=65535)
{
ulMeaTime = (unsigned long)gc_ucPCA_OverCnt * 65536l + g_uiStopTime.all; // 得到回波时间
if(ulMeaTime < INHIBIT_TIME)
{
// 如果时间没超过屏蔽时间,打开接收,继续计时
g_b852Inhibit = EN852OUT;
g_bUltraSonic_In = TRUE;
g_ucWorkStat = ULTRASONIC_R; // 回到接收状态
break;
}
else
{
// 正常数据处理
g_uc852GainCtrl = g_uc852GainCtrl&0xF0; // 如果到了,将增益回零
CCON = CCON&STOPPCA_C; // 停止 PCA 计时器
g_bCut_Off = CUT_OFF_INVALID; // 释放回波抑制
ulMeaTime = ulMeaTime*ULTRA_SPEED/TIME_RATIO; // 单位 :mm
ulMeaTime /=2; // 来回为 2 倍距离
}
}
else
{
ulMeaTime = 0xFFFFFFFF; // 置溢出标志
}
ga_uiMeaData[gi_ucDataSavePtr] = (unsigned int)ulMeaTime; // 保存测量数据
gi_ucDataSavePtr = (gi_ucDataSavePtr+1)&(DATA_SAVE_NUM-1); // 环形处理
if((g_ucWorkMode&GET_MAIN_MODE) == AUTO_MEASURE)
{
// 自动测量模式
g_ucWorkStat = DATA_BACK;
}
else
{
// 单次测量模式
gc_ucMeaTimesCnt--; // 测量计数
if(gc_ucMeaTimesCnt == 0)
{
g_ucWorkStat = DATA_BACK; // 要求的测量次数完成后,转换到数据返回状态
}
else
{
gc_ucGapAtSingleMea = MEA_GAPTIME;
g_ucWorkStat = WAIT_NEXT; // 没有完成,等待一个时间间隔后再启动下一次数据采集
}
}
break;
}
case DATA_BACK:
{
g_ucWorkDispStat = MEA_OK; // 完成一次测量
gc_ucWorkDispNumCnt = ga_ucWorkDispNum[g_ucWorkDispStat];
gc_uiWorkDispTimeCnt = ga_uiWorkDispTime[g_ucWorkDispStat];
switch(g_ucWorkMode)
{
case (AUTO_MEASURE + AUTO_BACK_DATA):
{
// 自动测量模式下自动数据返回处理
ga_ucTxdBuf[2] = g_ucSenderAddr; // 设置自动模式命令者作为接收方
ga_ucTxdBuf[4] = 3; // 帧长
ga_ucTxdBuf[5] = g_ucWorkMode; // 返回工作模式
sum = ga_ucTxdBuf[5];
uitemp.all = ga_uiMeaData[(gi_ucDataSavePtr-1)&(DATA_SAVE_NUM-1)];
ga_ucTxdBuf[6] = uitemp.b[1];
sum +=ga_ucTxdBuf[6];
ga_ucTxdBuf[7] = uitemp.b[0]; // 返回最新测量数据
sum += ga_ucTxdBuf[7];
ga_ucTxdBuf[8] = ~sum; // 校验和
gc_ucTxdCnt = 9; // 发送字节计数
gi_ucTxdPtr = 0; // 发送指针
SBUF = ga_ucTxdBuf[0]; // 启动发送
g_ucWorkStat = WAIT_FINISH; // 转换到等待数据返回结束状态
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -