任务需求
2018年安徽省机器人大赛单片机与嵌入式系统应用技能竞赛试题
- 使用可变电阻通过AD(10位)数模转换输入3位半数值(保留两位小数),模拟超声波传感器输入0-10米
- 模拟输入值大于1.2米的时候,输出控制电机正转,并实现数值越大转速越快,数值越小,转速越慢的效果。当小于0.9米的时候电机反转,小于0.5米的时候开始声光报警。
硬件介绍
1.可变电阻、AD转换
在本实验中,可变电阻与单片机的P1.0口连接,电路原理图如下所示:
在本实训平台所使用的是STC15W4K56S4单片机,其所属的15系列都有其内部的AD转换器。该内部A/D转换器可支持8路A/D采集,支持8位或10位的A/D分辨率,转换速度可达300KHz。
与A/D相关的寄存器配置
1.1 P1ASF P1口模拟功能控制寄存器
P1ASF模拟功能控制寄存器的配置用于确认P1的哪一个口与电位器相连接
1.2 ADC_CONTR:ADC控制寄存器
ADC_CONTR寄存器为ADC的主要寄存器,在该寄存器中配置ADC的电源、模数转换器速度、模数转换开启以及停止和模拟输入通道选择等相关配置。
该寄存器的相关介绍如下所示:
ADC_POWER:ADC电源控制位配置详解:
SPEED1、SPEED0:模数转换器转换速度控制位详解:
SPEED1 | SPEED0 | A/D转换所需时间 |
---|---|---|
1 | 1 | 90个时钟周期转换一次,CPU工作频率21MHz时,A/D转换速度约300KHz |
1 | 0 | 180个时钟周期转换一次 |
0 | 1 | 360个时钟周期转换一次 |
0 | 0 | 540个时钟周期转换一次 |
ADC_FLAG标志位详解:
模数转换器转换停止标志位,当AD转换结束后,ADC_FLAG = 1,要由软件清0。
不管A/D转换完成后由该位申请产生中断,还是由软件查询该标志位A/D转化是否结束,A/D转换完成后该位置1,一定要软件清零!!!
ADC_START标志位详解:
该位为模数转换器转换启动控制位,设置为“1”开始转换,转换结束后自动为“0”。
CHS2、CHS1、CHS0模拟输入通道选择位详解:
CHS2 | CHS1 | CHS0 | 模拟通道输入选择 |
---|---|---|---|
0 | 0 | 0 | P1.0作为A/D输入 |
0 | 0 | 1 | P1.1作为A/D输入 |
0 | 1 | 0 | P1.2作为A/D输入 |
0 | 1 | 1 | P1.3作为A/D输入 |
1 | 0 | 0 | P1.4作为A/D输入 |
1 | 0 | 1 | P1.5作为A/D输入 |
1 | 1 | 0 | P1.6作为A/D输入 |
1 | 1 | 1 | P1.7作为A/D输入 |
1.3 ADC_RES、ADC_RESL:A/D转换结果寄存器
2.步进电机
步进电机的驱动原理图如下所示:
步进电机就是能够通过输入脉冲的个数确定旋转的角位移。
步进电机由驱动芯片ULN2003驱动,利用ULN2003与单片机的引脚连接以驱动步进电机,需要注意的是,要驱动步进电机需要放大驱动电流。
引脚连接:
ULN2003 | 单片机引脚号 |
---|---|
ULN_O1 | P11 |
ULN_O2 | P12 |
ULN_O3 | P13 |
ULN_O4 | P14 |
硬件连接示意图
各元器件与MCU的引脚连接可以参照下图:
软件流程图
功能实现
需求1:可变电阻通过AD(10位)数模转换输入3位半数值(保留两位小数),模拟超声波传感器输入0-10米
实现步骤
- 获取ADC获取的结果
unsigned int GetADCResult(unsigned char ch)
{
unsigned int Vo;
P1ASF = 0x01; //选择P1口的哪一口 这里的口和ch要对应才能达到选择该口
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;//0x00|0x00|ch|0x08:选择A/D输入通道,开始A/D转换
// 这么用语句的主要原因就是不能位寻址
// 通道选择在后3位所以直接用一个整数表示ch
//例如ch=6 那么对应的后三位就是110
_nop_(); //Must wait before inquiry ,
_nop_(); //设置ADC_CONTR寄存器后需加4个CPU时钟周期的延时,才能保证值被写入ADC_CONTR寄存器
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG)); //Wait complete flag
ADC_CONTR &= ~ADC_FLAG; //Close ADC 将标志位清零等待下次硬件置1
//也可以写成 ADC_CONTR= ADC_CONTR & ( ~ADC_FLAG)
//Vo=(ADC_RES<<2)+ADC_RESL; //打开10位AD采集功能 如果用8位AD Vo=ADC_RESL 即可
Vo=(ADC_RES<<2); //10位AD采集 即2的10次方 满值为1024 这里用1024表示5伏的电压
//8位AD采集 即 2的8次方 满值为256 用256表示5伏
return Vo;
}
- 记录电位器模拟输入的最大值和最小值,由于其是线性变换,可以等效缩放到0-10这个范围。
ad=GetADCResult(0); //读取电压数量值P1.0口 采集AD电压值
num = (ad - 336)/67.6 * 100; //将获取到的值等效缩放到 0~10 具体问题具体分析
serial_one_send_string("AD距离:");
// 设置两位有效小数 以下输出为串口输出测试
serial_one_send_number(num / 1000 % 10);
serial_one_send_number(num / 100 % 10);
serial_one_send_string(".");
serial_one_send_number(num / 10 % 10);
serial_one_send_number(num / 1 % 10);
serial_one_send_string("米\r\n");
serial_one_send_number(KeyNum);
serial_one_send_string("\r\n");
- LCD12864显示
与上一篇显示超声波距离数值相同,不再赘述。
需求2: 模拟输入值大于1.2米的时候,输出控制电机正转,并实现数值越大转速越快,数值越小,转速越慢的效果。当小于0.9米的时候电机反转,小于0.5米的时候开始声光报警。
实现步骤:
- 设置步进电机正转以及反转的设置
uchar phasecw[4] ={0x10,0x08,0x04,0x02};//反转 电机导通相序 D-C-B-A
uchar phaseccw[4]={0x02,0x04,0x08,0x10};//正转 电机导通相序 A-B-C-D
//ms延时函数
void Delay_xms(uint x)
{
uint i,j;
for(i=0;i<x;i++)
for(j=0;j<224;j++);
}
//顺时针转动
void MotorCCW(uchar speed)
{
uchar i;
for(i=0;i<4;i++)
{
MotorData=phaseccw[i];
Delay_xms(150 - speed);//转速调节
}
}
//逆时针转动
void MotorCW(void)
{
uchar i;
for(i=0;i<4;i++)
{
MotorData=phasecw[i];
Delay_xms(50);//转速调节
}
}
//停止转动
void MotorStop(void)
{
MotorData=0x00;
}
- 不断进行比较,看模拟输入值符合哪个场景,执行对应操作
if(num > 120)
{
int i;
i=1000-c;
count = 0 ;
timer_count = 0 ;
//电机正转
MotorCCW(num); //将num值作为速度参数传入,实现模拟输入越大转速越快
//模拟输入越小,转速越小
}
if(num<90)
{
//电机反转
MotorCW();
}
if(num<50)
{
LED0=~LED0;
BEEP_IO = ~BEEP_IO;
delay(200);
}
总结:
经过这个练习之后,对串口调试输出的重要性有的不一般的理解,由于该实训平台所使用的STC15W4K56S4单片机不支持在线debug,因此只能直接烧录程序看现象是否符合预期。
但天不遂人愿,每次烧录结束后的结局都不是我想要的,但一时间也不知道是哪里出现了问题,比如在读取电位器模拟输入并在LCD12864上显示,屏幕上只有固定文字,丝毫不显示数据,完全不清楚是因为没有数据产生没显示还是现实的方式有问题。因此这时候就需要串口数据传输,辅助程序debug,获取的数据值先从串口打印输出,看是不是正确,再将数据交给显示屏显示。
以上就是本次练习的小小总结,我是王平凡,一个单片机小白,如有建议或疑问,欢迎留言(建议欢迎!疑问恕我菜鸡不一定能一一回答)。