资源描述
实验一 直流、步进电机综合控制系统设计
———————————————————————————————— 作者:
———————————————————————————————— 日期:
20
个人收集整理 勿做商业用途
实验一 直流、步进电机综合控制系统设计
一、实验目的
通过本次实验熟悉MagicARM2410 GPIO、UART、RTC,步进电机,直流电机,IIC的工作原理;并能熟悉的编程控制。
二、实验要求
以MagicARM2410为实验平台,设计一个通过串口通信交互控制的综合型控制系统.具体来说,可在启动时,自动列出一些选项,通过键盘选择某个选项而实现其功能;考虑到实时性,再按某个控制键能立刻停止当前功能而回到初始的待选状态。具体功能要求如下:
1、实现四个LED灯按照十六进制的规则依次亮灭;
2、在超级终端上输入两个两位数,实现两位数的加减乘除并显示运算结果;
3、显示当前的年月日、星期、时分秒;
4、实现步进电机的调速和正反转;可用旋转按钮W1或按键控制调速,在超级终端和数码管上同步显示其正反转和转速信息;
5、实现直流电机德调速和正反转;可用旋转按钮W2或按键控制调速,在超级终端和数码管上同步显示其正反转和转速信息;
6、利用IIC通信,实现EEPROM的读写;在超级终端上显示地址和数据。
三 、实验原理
(一)LED灯
四个小灯分别接于GPE11、GPE12 GPH4、GPH6端口;控制端口“0“和“1"分别实现亮灭(端口设为输出)如下:
void LED_init(void)
{
// 初始化I/O
rGPECON = (rGPECON & (~(0x0F<<22))) | (0x05〈<22); // rGPECON[25:22] = 0101b,设置GPE11、GPE12为GPIO输出模式
rGPHCON = (rGPHCON & (~(0x33〈〈8))) | (0x11<<8); // rGPHCON[13:8] = 01xx01b,设置GPH4、GPH6为GPIO输出模式
}
(二)UART
UART是一种通用串行数据总线,用于异步通信.该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用来与PC进行通信,包括与监控调试器和其它器件,如EEPROM通信。UART首先将接收到的并行数据转换成串行数据来传输。消息帧从一个低位起始位开始,后面是5~8个数据位,一个可用的奇偶位和一个或几个高位停止位。接收器发现开始位时它就知道数据准备发送,并尝试与发送器时钟频率同步.如果选择了奇偶,UART就在数据位后面加上奇偶位。奇偶位可用来帮助错误校验。 在接收过程中,UART从消息帧中去掉起始位和结束位,对进来的字节进行奇偶校验,并将数据字节从串行转换成并行。UART也产生额外的信号来指示发送和接收的状态。例如,如果产生一个奇偶错误,UART就置位奇偶标志。
UART作用
1. 发送/接收逻辑
2. 波特率的产生
3. 数据收发
4. 中断控制{出现以下情况时,可使UART 产生中断:
FIFO 溢出错误 线中止错误(line—break,即Rx 信号一直为0 的状态,包括校验位和停止位在内) 奇偶校验错误 帧错误(停止位不为1) 接收超时(接收FIFO 已有数据但未满,而后续数据长时间不来) 发送 接收 由于所有中断事件在发送到中断控制器之前会一起进行“或运算”操作,所以任意时刻 UART 只能向中断产生一个中断请求.通过查询中断状态函数UARTIntStatus( ),软件可以在同一个中断服务函数里处理多个中断事件(多个并列的if 语句)。
5 回环操作
6 串行红外协议
UART初始化函数如下
void UART_Init(void)
{
int i;
if(g_uart_sel) // 判断是否为串口1
{
// I/O口设置 (GPH5,GPH4)
rGPHUP = rGPHUP | (0x03〈〈4);
rGPHCON = (rGPHCON & (~0x00000F00)) | (0x00000A00);
// 串口模式设置
rUFCON1 = 0x00; // 禁止FIFO功能
rUMCON1 = 0x00; // AFC(流控制)禁能
rULCON1 = 0x03; // 禁止IRDA,无奇偶校验,1位停止位,8位数据位
rUCON1 = 0x245; // 使用PCLK来生成波特率,发送中断为电平触发模式,接收中断为边沿触发模式,
// 禁止接收超时中断,使能接收错误中断,正常工作模式,中断或查询方式(非DMA)
// 串口波特率设置
rUBRDIV1=(int)(PCLK/16.0/UART_BPS + 0。5) -1;
}
else
{
// I/O口设置 (GPH3,GPH2)
rGPHUP = rGPHUP | (0x03〈<2);
rGPHCON = (rGPHCON & (~0x000000F0)) | (0x000000A0);
// 串口模式设置
rUFCON0 = 0x00; // 禁止FIFO功能
rUMCON0 = 0x00; // AFC(流控制)禁能
rULCON0 = 0x03; // 禁止IRDA,无奇偶校验,1位停止位,8位数据位
rUCON0 = 0x245; // 使用PCLK来生成波特率,发送中断为电平触发模式,接收中断为边沿触发模式,
// 禁止接收超时中断,使能接收错误中断,正常工作模式,中断或查询方式(非DMA)
// 串口波特率设置
rUBRDIV0=(int)(PCLK/16。0/UART_BPS + 0.5) —1;
}
for(i=0;i<100;i++);
}
(三)RTC
A 概述
实时时钟(RTC)单元在系统电源关闭的情况下可以在备用电池下工作。RTC 可以使用STRB/LDRB ARM 操作传输二进制码十进制数的 8 位数据给 CPU。数据包括秒、分钟、小时、日期、天、月、年的时间信息。RTC 单元可以在 32.768KHz 的外部晶振下工作,可以可以执行报警功能。
B 特点
- BCD 数:秒、分钟、小时、日期、日、月、年
— 闰年生成器
— 报警功能:报警中断或从掉电模式中唤醒
- 已经解决 2000 年问题
- 独立电源引脚(RTCVDD)
— 支持对于实时内核时间节拍的毫秒节拍时间中断
C 实时时钟操作
C.1 闰年发生器
闰年发生器可以基于 BCDDATE、BCDMON、BCDYEAR 的数据,从 28、29、30、31 中确定每个月的最后一天.该模块在确定某月最后一天的时候会考虑闰年的因素。一个 8 位的计数器仅能代表两个 BCD 数字,所以它不能确定是否是 00 年(该年的最后两个数字是00)。
C。2读写寄存器
为了写RTC模块中的BCD寄存器,RTCCON寄存器的位 0 必须置 1。为了显示秒分小时星期日月年,CPU应该分别读取在RTC模块中的BCDSEC,BCDMIN,BCDHOUR,BCDDAY,BCDDATE,BCDMON,和BCDYEAR。但是,因为多寄存器读取可能存在一秒的误差。例如,当用户读BCDYEAR和BCDMON,结构假定是 2059 年 12 月 31 日 23 点 59 分。当用户读BCDSEC寄存器,值的范围是从 1 到 59(秒)就没有问题,但是如果值是 0,年月日就变成了 2060 年 1 月 1 日0 时 0 分因为有刚才提到的 1 秒误差.在这种情况下如果BCDSEC为 0,用户应该重读BCDYEAR到BCDSEC。
C。3 备用电池操作
RTC 逻辑可以由备用电池驱动,其通过 RCTVDD 引脚给 RTC 模块提供电源,即使系统电源关闭。当系统关闭时,CPU 和 RTC 模块的接口是封闭的,备用电池仅驱动振荡电路和BCD 计数器以最小化电源消耗。
C。4 报警功能
RTC 在掉电模式或正常操作模式下的特定时间会发出报警信号。在正常操作模式下报警中断(INT_RTC)被激活。在掉电模式下,电源管理唤醒信号(PMWKUP)也如 INT_RTC一样被激活。RTC 报警寄存器(RTCALM)决定了报警的使能状态和报警时间设定的条。
C.5 节拍时间中断
RTC 节拍时间是用于中断请求。TICNT 寄存器有一个中断使能位和对于中断的计数器值当节拍时间中断出现时,计数器的值为 0。中断周期如下:Period = ( n+1 ) / 128 secondn: 节拍计数器值 (1~127)RTC 节拍时间可以用于实时操作系统内核时间节拍。如果时间节拍由 RTC 时间节拍生成,与实时操作系统功能相关的时间就会和实时同步。
D。RTC寄存器
(1)实时时钟控制寄存器(RTCCON)
(2)节拍时间计数寄存器(TICNT)
(3)RTC 报警控制寄存器(RTCALM)
(4)报警秒数据寄存器(ALMSEC)
(5)报警分钟数据寄存器(ALMMIN)
(6)报警小时数据寄存器(ALMHOUR)
(7)报警日期数据寄存器(ALMDATE)
(8)报警月数据寄存器(ALMMON)
(9)报警年数据寄存器(ALMYEAR)
(10)BCD 秒寄存器(BCDSEC)
(11)BCD 分寄存器(BCDMIN)
(12)BCD 小时寄存器(BCDHOUR)
(13)BCD 日期寄存器(BCDDATE)
(14)BCD 日寄存器(BCDDAY)
(15)BCD 月寄存器(BCDMON)
(16)BCD 年寄存器(BCDYEAR)
(四)步进机与直流电机工作原理
步进电机是一种将电脉冲转化为角位移的执行机构。步进电机正传,双四拍程序。 时序控制为AB——BC--CD——DA--AB,共控制运转4圈(电机步距角为18度)。 每一步的延时控制。值越大,延时越久;步进电机反转,双四拍程序时序控制为AD--DC--CB—-BA——AD。步距角为18度)。每一步的延时控制。值越大,延时越久。
正转模式:dly为延时
void MOTO_Mode1(uint32 dly)
{
// AB相有效
GPIOSET(MOTOA);
GPIOSET(MOTOB);
Delay(dly);
GPIOCLR(MOTOA);
GPIOCLR(MOTOB);
// BC相有效
GPIOSET(MOTOB);
GPIOSET(MOTOC);
Delay(dly);
GPIOCLR(MOTOB);
GPIOCLR(MOTOC);
// CD相有效
GPIOSET(MOTOC);
GPIOSET(MOTOD);
Delay(dly);
GPIOCLR(MOTOC);
GPIOCLR(MOTOD);
// DA相有效
GPIOSET(MOTOD);
GPIOSET(MOTOA);
Delay(dly);
GPIOCLR(MOTOD);
GPIOCLR(MOTOA);
}
直流电机由PWM脉冲调制控制;脉冲为1是电机转动,0时停止转动。通过PWM占空比来实现加减速.参考程序:
void StepMOTO_Test(void)
{
// uint32 a = 0;
// 步进电机控制口设置
rGPCCON = (rGPCCON & (~0x0000FC03)) | (0x00005401); // GPC0、GPC5——7口设置为输出
rGPCUP = rGPCUP | 0x00E1; // 禁止GPC0、GPC5——7口的上拉电阻
rGPCDAT = rGPCDAT & (~0x00E1); // 设置GPC0、GPC5—-7口输出低电平
while(1)
{
UART_SendStr(”Please input 'z' OR ’f’ to control zhengzhuan and fanzhuan\n");
UART_SendStr(”Press 'o' key exit test\n");
switch(getch = UART_GetKey())
{
case 'z':
{
sprintf("Press ’j’ to speed up ; ’ESC' break out.”);
for(;;)
{
Change_Speed();
MOTO_Mode1(speed1); // 控制步进电机正转
//在终端上显示速度
sprintf(disp_buf2, "Zhengzhuan: StepMoto_speed is %d \n”, (330—speed1));
UART_SendStr(disp_buf2);
if(UART_GetFlag() == 'j’)
speedup();//加速
if(UART_GetFlag() == 0x1B) //按Esc键退出测试
break;
}
break;
}
case 'f’:
{
sprintf(”Press ’j' to speed up ; 'ESC’ break out。”);
for(; ;)
{
Change_Speed();
MOTO_Mode2(speed1); // 控制步进电机反转
//在终端上显示速度
sprintf(disp_buf2, ”Fanzhuan: StepMoto_speed is %d \n”, —(330—speed1));
UART_SendStr(disp_buf2);
if(UART_GetFlag() == ’j’)
UART_SendStr("j");
speedup();//加速
if(UART_GetFlag() == 0x1B) //按Esc键退出测试
break;
}
break;
}
default: break;
}
if(getch == 'o’) //按下字母’o’键退出整个步进电机测试
break;
}
}
直流电机测试程序:
void DCMOTO_Test(void)
{
uint32 i = 0, a = 0;
uint16 pwm_dac;
char get_ch = 0;
// TOUT0口设置
rGPBCON = (rGPBCON & (~(0x03<〈0))) | (0x02〈<0); // rGPBCON[1:0] = 10b,设置TOUT0功能
rGPBUP = rGPBUP | 0x0001; // 禁止TOUT0口的上拉电阻
// 设置GPH9为GPIO输出模式
rGPHCON = (rGPHCON & (~(0x03<〈18))) | (0x01<〈18); // GPH9口
rGPHDAT = rGPHDAT & (~(1〈<9)); // 输出0电平
rGPHUP = rGPHUP | (1〈〈9);
// 初始化PWM输出.设PWM周期控制值为255
pwm_dac = 250*255/330; // 初始化占空比
PWM_Init(255, pwm_dac);
while(1)
{
//按’z'键控制正传,'f’键控制反转,’s'键停止转动 ’Esc'退出测试
UART_SendStr("\nPress the key to control state: ’z'——zhengzhuan 'f’--fanzhuan ’s'--stop\n”);
switch((get_ch =UART_GetKey()))
{
case ’z’:
{
rGPBDAT = rGPHDAT | (1<<9);
rGPHDAT = rGPHDAT & (~(1〈<9));
while(1)
{
Change_Speed();
rTCMPB0 = (speed2*255/330);
sprintf(disp_buf3, ”DCMoto_speed is %d \n", speed2); //终端显示正转速度
UART_SendStr(disp_buf3);
if(UART_GetFlag() == 's’)
{
rGPBDAT = rGPHDAT & (~(1〈<9));
rGPHDAT = rGPHDAT & (~(1〈<9));
break;
}
}
break;
}
case ’f’:
{
rGPBDAT = rGPHDAT & (~(1〈<9));
rGPHDAT = rGPHDAT | (1〈<9);
while(1)
{
Change_Speed();
rTCMPB0 = (speed2*255/330);
sprintf(disp_buf3, "DCMoto_speed is %d \n", (-speed2)); //终端显示正转速度
UART_SendStr(disp_buf3);
if(UART_GetFlag() == 's')
{
rGPBDAT = rGPHDAT & (~(1〈〈9));
rGPHDAT = rGPHDAT & (~(1<<9));
break;
}
}
break;
}
default : break;
}
if(UART_GetFlag() == 0x1B)
break;
}
}
(五)IIC通信实现EEPROM的读写
(一)I2C,一种总线结构.I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上.
实验过程中必须先初始化总线—启动总线后才能用其进行通信。
int I2C_SendByte(uint8 dat) //发送一字节数据,并接收应答位
int I2C_RcvByteNA(uint8 *dat)// 接收I2C总线上一字节数据并发送非应答位
int I2C_RcvByteA(uint8 *dat)// 接上I2C总线上一字节数据,并发送应答位
void StopI2C(uint8 send)//结束总线
int ISendStr(uint8 sla, uint8 *suba, uint8 *s, uint8 no)
【** Descriptions: 使用硬件I2C发送数据。
** Input: sla 从机地址
** suba 器件子地址(第一字节用来表示子地址字节个数)
** s 发送数据缓冲区
** no 发送数据个数
** Output: 操作成功返回TRUE,仲载失败/无从机应答返回FALSE
** Note: 使用前设置好参数。程序不会更改s、suba缓冲区的数据】
int IRcvStr(uint8 sla,uint8 *suba,uint8 *s,uint8 no)
//v使用硬件I2C读取数据.参数和发送对应
(二)测试EEPROM程序
void TestEEPROM(void)
{
uint8 suba[2];
int rw_err;
int i;
rw_err = 0;
// 写数据测试
suba[0] = 1; // 1位子地址
suba[1] = 0x00; // 子地址
for(i=0; i<10; i++)
{
dat_buf[i] = i+’0’;
}
UART_SendStr(”data of write:”);
for(i = 0; i 〈 5; i++)
UART_SendByte(dat_buf[i]);
UART_SendStr(”\n");
i2c_opsta=ISendStr(CAT1025, suba, dat_buf, 5);
for(i=0; i〈10000; i++); // 等待写周期
// 读出校验
for(i=0; i〈5; i++) dat_buf[i] = 0x00;
i2c_opsta=IRcvStr(CAT1025, suba, dat_buf, 5);
UART_SendStr(”data of read:");
for(i = 0; i 〈 5; i++)
UART_SendByte(dat_buf[i]);
UART_SendStr("\n”);
for(i=0; i<5; i++)
{
if(dat_buf[i] != (i+’0')) rw_err = 1;
}
// 判断操作是否出错,如果出错则蜂鸣报警
if(rw_err) ErrorShow();
}
【如果写入的数据和读出的相同,则测试通过】
四、实验程序
【main函数:主要实现与终端交互的功能,通过键盘输入字符的判断来决定执行的功能】
int main(void)
{
uint8 g_getch ;
int i = 0;
UART_Select(0); //选择UART0
UART_Init(); //UART0初始化
LED_init(); //LED相关初始化
EINT_init(); //外部中断初始化
while(1)
{
fun_test();
switch(g_getch = UART_GetKey())
{
case '1':
{
UART_SendByte(g_getch);
UART_SendStr(”\n”);
LED_DispAllOn(); //LED灯全亮
DelayNS(10);
UART_SendStr("\nTest LED_DispAllOn is Over!\n");
break;
}
case ’2':
{
UART_SendByte(g_getch);
UART_SendStr(”\n");
LED_DispAllOff(); //LED灯全灭
DelayNS(10);
UART_SendStr(”\nTest LED_DispAllOff is Over!\n”);
break;
}
case ’3':
{
UART_SendByte(g_getch);
UART_SendStr("\n”);
for(;;) //死循环,当检测到按下‘Esc’键时退出
{
LED_onebyone(); //流水灯
if(UART_GetFlag() == 0x1B) //检测是否按下’Esc'键
break;
}
LED_DispAllOff();
UART_SendStr(”\nTest LED_OnebyOne is Over!\n”);
break;
}
case '4’:
{
UART_SendByte(g_getch);
UART_SendStr("\n");
Arithmitic(); //加减乘除测试模块
UART_SendStr("\nTest Addsubmuldiv_Test is Over!\n");
break;
}
case ’5’:
{
UART_SendByte(g_getch);
UART_SendStr(”\n");
DateWeekTime_Test();
UART_SendStr(”\nTest DateWeekTime_Test is Over!\n");
break;
}
case '6’:
{
UART_SendByte(g_getch);
UART_SendStr("\n”);
StepMOTO_Test();
UART_SendStr(”\nTest StepMOTO_Test is Over!\n”);
break;
}
case '7':
{
UART_SendByte(g_getch);
UART_SendStr(”\n”);
DCMOTO_Test();
UART_SendStr("\nTest DCMOTO_Test is Over!\n”);
break;
}
case ’8':
{
UART_SendByte(g_getch);
UART_SendStr("\n");
IIC_Test();
UART_SendStr(”\nTest IIC_Test is Over!\n”);
break;
}
case ’9':
{
return 0;
}
default: break;
}
}
return 0;
}
五、思考讨论题或体会或对改进实验的建议
通过这个综合实验,不仅熟悉了Magic2410各部分的工作原理,学习了如何去控制各部件,而且也学到了很多C的技巧(比如:#define SUM(a,b) ((a)+(b))等等在算术运算中宏的使用可以使函数既简洁又通用);还有看了那些底层驱动之后,知道了驱动的与 应用的本质区别:驱动实现所有基本功能,而不对上层应用加以限制,要注意它的技巧性与简洁性.
展开阅读全文