实验三:直流电机的驱动
Download
1 / 59

实验三:直流电机的驱动 与编码器脉冲的采集 - PowerPoint PPT Presentation


  • 101 Views
  • Uploaded on

实验三:直流电机的驱动 与编码器脉冲的采集. 内容概要. 学习 1 种 ATmega64 资源 —— 定时、计数器 3 演示 2 个实验: ( 1 )直流电机的驱动、调速 ( 2 )上述实验的软件仿真 自行做 3 个实验: ( 1 )直流电机的驱动 ( 2 )直流电机的 PWM 调速 ( 3 )光电编码器脉冲信号的读取. 控制什么?. 控制什么?. 用到哪些单片机资源?. 单片机模块 —— 电机控制. 通信模块 —— 和 PC 通信. 电机 驱动 模块 —— 驾驭电机. 电源模块 —— 提供动力.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' 实验三:直流电机的驱动 与编码器脉冲的采集' - niesha


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

实验三:直流电机的驱动 与编码器脉冲的采集


内容概要

  • 学习1种ATmega64资源 ——定时、计数器3

  • 演示2个实验:

  • (1)直流电机的驱动、调速

  • (2)上述实验的软件仿真

  • 自行做3个实验:

  • (1)直流电机的驱动

  • (2)直流电机的PWM调速

  • (3)光电编码器脉冲信号的读取




用到哪些单片机资源?

单片机模块——

电机控制

通信模块——和PC通信

电机

驱动

模块——驾驭电机

电源模块——提供动力

输入输出模块——按键控制和状态指示


让直流电机动起来!

  • M+和M-分别接电源和地,电机就可以转动

  • 交换M+和M-,电机就反向转动


L298驱动直流电机


L298驱动直流电机


自己动手之一——驱动直流电机

  • 指令的输入:通过按键

  • 按钮Stop、F.W.、B.W.作为控制按钮

  • Stop(PC3,可触发外部中断INT5):启动、停止

  • F.W.、B.W.( PC0、PC1,可触发外部中断4):正、反转


调试灯

  • 调试用LED灯,可表示正、反转状态,运行状态等,也可以自定义。


电机控制I/O

  • PB4、PB5与L298的输入引脚相连

  • 注意:小心滑块撞到支架!


操作说明

  • 进行电机实验前,确保各接口连接好后,再打开电源。

  • 在使用电机模块时,先打开电源开关,再打开L298使能开关;关闭时,先关闭L298使能开关,再关闭电源开关。

电源开关

L298使能开关



标志位的设定

  • #define RUN 1

  • #define STOP 0

  • #define FORWARD 0

  • #define BACKWARD 1

  • unsigned char DC_State = STOP; // 电机状态,STOP或RUN

  • unsigned char dir_flag = FORWARD; // 方向标志,FORWARD或BACKWARD


宏定义

  • #define isButtonFWDown() ((PINC&0B00000001)==0)

  • #define isButtonBWDown() ((PINC&0B00000010)==0)

  • #define isButtonZERODown() ((PINC&0B00000100)==0)

  • #define isButtonSTOPDown() ((PINC&0B00001000)==0)

  • #define LedFWOn() (PORTA &= 0B11111110)

  • #define LedBWOn() (PORTA &= 0B11111101)

  • #define LedZeroOn() (PORTA &= 0B11111011)

  • #define LedRunOn() (PORTA &= 0B11110111)

  • #define LedFWOff() (PORTA |= 0B00000001)

  • #define LedBWOff() (PORTA |= 0B00000010)

  • #define LedZeroOff() (PORTA |= 0B00000100)

  • #define LedRunOff() (PORTA |= 0B00001000)


自定义函数

  • void DC_Forward()

  • {

  • PORTB |= 0B00010000;

  • PORTB &= 0B11011111;

  • }

  • void DC_Backward()

  • {

  • PORTB |= 0B00100000;

  • PORTB &= 0B11101111;

  • }

  • void DC_Stop()

  • {

  • PORTB &= 0B11001111;

  • }

  • 使用宏定义与自定义函数,可以提高程序的可读性与可维护性。

  • 而宏定义与自定义函数相比,程序的执行效率更高。


外部中断4的处理程序举例

  • #pragma interrupt_handler int4_isr:6

  • void int4_isr()

  • {

  • //external interupt on INT4

  • delay_ms(50); // 软件防抖

  • if ( DC_State == RUN ) {

  • if ( isButtonFWDown() )

  • {

  • dir_flag = FORWARD;

  • LedFWOn();

  • LedBWOff();

  • }

  • else if ( isButtonBWDown() )

  • {

  • dir_flag = BACKWARD;

  • LedBWOn();

  • LedFWOff();

  • }

  • }

  • }


外部中断5的处理程序举例

  • #pragma interrupt_handler int5_isr:7

  • void int5_isr()

  • {

  • //external interupt on INT5

  • delay_ms(50);

  • if ( isButtonSTOPDown() ) {

  • if ( DC_State == RUN )

  • {

  • DC_State = STOP;

  • LedRunOff();

  • DC_Stop();

  • }

  • else if ( DC_State == STOP )

  • {

  • DC_State = RUN;

  • LedRunOn();

  • }

  • }

  • else if ( isButtonZERODown() )

  • {

  • }

  • }


主函数

  • while(1)

  • {

  • if (DC_State == RUN )

  • {

  • if ( dir_flag == FORWARD )

  • DC_Forward();

  • else if ( dir_flag == BACKWARD )

  • DC_Backward();

  • }

  • }


如何改变电机的转速?

  • 对L298的控制有单极性、双极性2种方式。

  • 单极性工作方式指的是在一个PWM周期内电机的电枢只承受单极性的电压;

  • 双极性工作方式是指在一个PWM周期内电机电枢两端的电压呈正负交替变化。


PWM

H

M+

L

转动时间

停止时间

电机正转

电机调速(单极性)

  • 以电机正转为例,在M+端产生PWM波,M-接低电平。则M+为高电平时,电机正转;为低电平时,电机停转。

  • 改变PWM波的占空比即可改变电机的转速,占空比越大,电机转速越快。

  • 脉冲宽度(简称脉宽),就是高电平的时间。

PWM(Pulse Width Modulation):脉冲宽度调制


Pwm t c1
如何产生PWM波?(T/C1)

  • 16位的T/C 可以实现精确的程序定时(事件管理)、波形产生和信号测量。其主要功能如下:

  • 用作定时器:可以用作频率发生器等;

  • 用作计数器:可用来对外部引脚的脉冲信号进行计数;

  • 输入捕获功能:可用来测定外部信号的脉宽;

  • 输出比较匹配功能:可用来产生脉宽调制信号(PWM波)。


相关引脚

  • T1(PD6)

  • OC1A(PB5)

  • OC1B(PB6)

  • OC1C(PB7)

  • ICP1(PD4)



有关定时/计数器的几个变量


M+

转动时间

停止时间

电机正转

电机调速(单极性,快速PWM模式)

TOP

OCR1A

TCNT1

溢出时置位,并进行

电机转向控制

BOTTOM

TOP(ICR1)

OCR1A

比较匹配时清零


软件设计

  • PWM波形产生方法:

    我们采用大致20KHz以上的PWM波进行电机的调速,采用Timer1的模式14,快速PWM模式,分频为1。使能overflow interrupt 和compare A interrupt中断,选择OC1A output mode为disconnected普通模式,在匹配中断中清零PB4、PB5,溢出中断中根据方向标志置位PB4或PB5。

    这样就用单极性方式产生了方向可变的PWM波形。


快速PWM模式

  • TOP:用来定义通过波形发生器产生的波形的周期(频率)。我们要求在20kHz,采用快速PWM模式(模式14),TOP值由ICR1寄存器给定。

  • OCR1A:输出比较寄存器,记录输出比较的脉冲数,即调节占空比。这样,如果可以随时改变OCR1A的值,就可以产生可调占空比的PWM波。在模式14中,OCR1A的更新在TCNT1达到TOP时刻。


输出比较中断

OCR1A

=?

TCNT1←TCNT1+1

比较匹配的标志位

OCF1A置位

(TIFR)

产生PWM

波形可从引脚

OC1A输出

总开关I打开?

(SREG)

分开关OCIE1A

打开?(TIMSK)

输出比较

中断服务

清空

OCF1A


输出比较单元

  • 双缓冲输出比较寄存器OCR1A一直与TCNT1 的值做比较。

  • 波形发生器用比较结果产生PWM或在输出比较引脚OC1A输出可变频率的信号。

  • 比较匹配结果还可置位比较匹配标志OCF1A,用来产生输出比较中断请求。


相关寄存器

  • SREG(状态寄存器)

  • Bit 7 – I: 全局中断使能(总开关)

  • I 置位时使能全局中断。单独的中断使能由其他独立的控制寄存器控制。如果I 清零,则不论单独中断标志置位与否,都不会产生中断。


相关寄存器

  • TIMSK(定时器中断屏蔽寄存器)(分开关)

  • Bit 5 – TICIE1: T/C1 输入捕捉中断使能

  • Bit 4 – OCIE1A:T/C1 输出比较 A 匹配中断使能

  • Bit 3 – OCIE1B:T/C1 输出比较 B 匹配中断使能

  • Bit 2 – TOIE1:T/C1 溢出中断使能


相关寄存器

  • TIFR(定时器中断标志寄存器)

  • Bit 5 – ICF1: T/C1 输入捕捉标志

  • Bit 4 – OCF1A: T/C1 输出比较 A 匹配标志

    当TCNT1 与OCR1A 匹配成功时,该位被设为"1”。

  • Bit 3 – OCF1B: T/C1 输出比较 B 匹配标志

  • Bit 2 – TOV1: T/C1 溢出标志


相关寄存器

  • TCCR1A(T/C1控制寄存器A)

  • TCCR1B(T/C1控制寄存器B)

  • TCCR1C(T/C1控制寄存器C)


Tccr1a t c1 a
TCCR1A(T/C1控制寄存器A)


Tccr1b t c1 b
TCCR1B(T/C1控制寄存器B)


Tccr1c t c1 c
TCCR1C(T/C1控制寄存器C)

  • • Bit 7 – FOCnA: 强制输出比较通道A

  • • Bit 6 – FOCnB: 强制输出比较通道B

  • • Bit 5 – FOCnC: 强制输出比较通道C


相关寄存器(5个16位)

  • TCNT1(TCNT1H、TCNT1L) (16位的T/C1定时数据寄存器)

  • OCR1A(OCR1AH、OCR1AL) (16位的T/C1双缓冲输出比较寄存器A)

  • OCR1B(OCR1BH、OCR1BL) (16位的T/C1双缓冲输出比较寄存器B)

  • OCR1C(OCR1CH、OCR1CL) (16位的T/C1双缓冲输出比较寄存器C)

  • ICR1(ICR1H、ICR1L)(16位的T/C1输入捕获寄存器)


自己动手之二——改变电机转速

  • Q:如何改变转速?

  • A:通过串口改变OCR1A的值,从而改变PWM占空比。

  • 通过串口由PC机改变OCR1A的值,从而改变直流电机的转速!


单极性方式

  • 输入端In1为PWM信号,输入端In2为低电平,电动机正转;输入端In2为PWM信号,输入端In1为低电平,电动机反转。

  • 占空比为0%时制动,为100%时达到最高速;

  • 当EnA为低电平时,驱动桥路上的4个晶体管全部截止,使正在运行的电动机电枢电流反向,电动机自由停止。

  • 但是,控制板的使能端通过按键开关接电源——用急停方式


Iccavr t c1
ICCAVR中如何设置T/C1

采用普通方式。

电机采用单极性方式驱动


定时/计数器1的初始化举例

  • void timer1_init(void)

  • {

  • TCCR1B = 0x00; //stop

  • TCNT1H = 0x00; //setup

  • TCNT1L = 0x00;

  • OCR1AH = 0x02;

  • OCR1AL = 0x27;

  • OCR1BH = 0x02;

  • OCR1BL = 0x27;

  • OCR1CH = 0x02;

  • OCR1CL = 0x27;

  • ICR1H = 0x02; // 20kHz

  • ICR1L = 0x27;

  • TCCR1A = 0x02; // OCR1A未连接,做普通端口操作

  • TCCR1B = 0x19; // start Timer

  • }

( 0227 )H=( 551 )D


定时/计数器1的比较匹配中断

  • #pragma interrupt_handler timer1_compa_isr:iv_TIM1_COMPA

  • void timer1_compa_isr(void)

  • {

  • //compare occured TCNT1=OCR1A

  • DC_Stop();

  • }


定时/计数器1的溢出中断处理

  • #pragma interrupt_handler timer1_ovf_isr:iv_TIM1_OVF

  • void timer1_ovf_isr(void)

  • {

  • //TIMER1 has overflowed

  • TCNT1H = 0x00; //reload counter high value

  • TCNT1L = 0x00; //reload counter low value

  • if (DC_State == RUN )

  • {

  • if ( dir_flag == FORWARD )

  • DC_Forward();

  • else if ( dir_flag == BACKWARD )

  • DC_Backward();

  • }

  • else DC_Stop();

  • }


通信协议

  • 每帧包含3字节数据:

  • 字节0:0xaa (帧开始标志)

  • 字节1:速度高4位(bit3~bit0)

  • 字节2:速度低8位

  • 速度范围(0x0001~0x0227),参见TIMER1初始化中ICR1取值


Iccavr usart1
ICCAVR中如何设置USART1


串口通信程序举例

  • unsigned char ReceivedData[3]; // 接收缓存

  • unsigned char receive_flag=0; // 接收完成标志,1表示接收一帧结束

  • #pragma interrupt_handler uart1_rx_isr:iv_USART1_RXC

  • void uart1_rx_isr(void)

  • {

  • static unsigned char rece_num=0;

  • unsigned char data;

  • data=UDR1;

  • if(( 0xAA == data )&&( 0 == rece_num ) || rece_num > 0 )

  • { ReceivedData[ rece_num ] = data;

  • rece_num++; }

  • if ( 3 == rece_num )

  • { rece_num = 0;

  • receive_flag = 1; }

  • }


主函数中修改脉冲宽度

  • while(1)

  • {

  • if (receive_flag == 1)

  • {

  • OCR1AH = ReceivedData[ 1 ];

  • OCR1AL = ReceivedData[ 2 ];

  • DC_State = RUN;

  • receive_flag = 0;

  • }

  • }


Proteus
PROTEUS仿真效果



读取编码器的脉冲

  • 增量式编码器输出的是连续的脉冲信号,通过得到一秒钟内的脉冲个数,利用已知的编码器线数(如我们用的编码器为500线)可得到电机转速,或者直接以每秒的脉冲数作为速度,具体以计算方便为准。

  • 对此有两种较为常见的做法:测频率和测脉宽法。

  • 我们采用测频率法(或测周期法),该方法是设定一个固定的采样时间T,比如1秒,采样一次得到的脉冲个数N,那么速度为

  • V=N/T ( pulse / sec )


自己动手之三——测得编码器脉冲数

  • 1. 测得直流电机在最大的转速下,20ms内的脉冲数。

  • 2.利用串口调试助手读取该脉冲数,并估算大致范围。


如何分配单片机资源?

  • T/C0——产生20ms定时

  • T/C1——驱动电机

  • T/C3——采集编码器脉冲 ,上升沿

  • USART1——串口通信

  • Q:20ms内编码器脉冲有多少?

  • A:16位的T/C3。(为什么?)

  • Q:何时采集编码器脉冲?

  • A:20ms计时溢出中断服务中。


串口协议的修改

  • 采用问答式发送上个20ms内编码器读数,对串口协议做了如下修改:

  • 字节1的第7位为1作为查询编码器读数的命令

  • 增加一个发送标志位:

  • unsigned char txd_flag=0; // 为1表示发送编码器读数


定时/计数器0中断服务程序

  • #pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF

  • void timer0_ovf_isr(void)

  • {

  • unsigned int encoder;

  • TCNT0 = 0x29; //reload counter value

  • encoder = TCNT3;

  • TCNT3 = 0;

  • if (txd_flag==1)

  • {

  • UDR1 = 0XAA;

  • while ( !( UCSR1A & (1<<UDRE1)) );

  • UDR1 = encoder>>8;

  • while ( !( UCSR1A & (1<<UDRE1)) );

  • UDR1 = encoder;

  • while ( !( UCSR1A & (1<<UDRE1)) );

  • txd_flag=0;

  • }

  • }


接收命令的执行

  • while(1)

  • {

  • if (receive_flag == 1)

  • {

  • if ((ReceivedData[1]&0x80)!=0)

  • {

  • txd_flag=1;

  • ReceivedData[ 1 ]-=0x80;

  • }

  • OCR1AH = ReceivedData[ 1 ];

  • OCR1AL = ReceivedData[ 2 ];

  • DC_State = RUN;

  • receive_flag = 0;

  • }

  • }


思考几个问题

  • 尝试一下用其他PWM模式驱动电机,比较各种模式的优缺点。

  • 用单极性方式和双极性方式产生PWM波的优缺点各是什么?

  • 为什么选择20ms定时?是否可以选择其他的时间长度?

  • 为什么使用16位的定时/计数器?可以用8位的吗?


实践大作业二分组报名

  • 121名同学

  • 5套直线运动模块

  • 实验安排:分四次,每次5组,时间1.5小时



ad