×
单片机 > 单片机程序设计 > 详情

基于PIC单片机的机械臂制作教程

发布时间:2020-06-13 发布时间:
|

步骤1:电路图

电路图非常简单;整个项目由12V适配器供电。然后使用两个7805电压调节器将此12V转换为+ 5V。一个标记为+ 5V,另一个标记为+ 5V(2)。具有两个调节器的原因是当伺服旋转时,它会吸入大量电流,从而产生电压降。这种电压降迫使PIC重新启动,因此我们无法在同一+ 5V电压轨上同时运行PIC和伺服电机。因此标有+ 5V的电源用于为PIC单片机,LCD和电位计供电,标有+ 5V(2)的独立稳压器输出用于为伺服电机供电。


提供0V至5V可变电压的电位器的五个输出引脚连接到PIC的模拟引脚An0至AN4。由于我们计划使用定时器来产生PWM,因此伺服电机可以连接到任何GPIO引脚。我为伺服电机选择了RD2到RD6的引脚,但它可以是您选择的任何GPIO。


由于程序涉及大量调试,因此16x2 LCD显示器也连接到PIC的portB。这将显示正在控制的伺服电机的占空比。除此之外,我还扩展了所有GPIO和模拟引脚的连接,以防将来需要连接任何传感器。最后,我还连接了编程器引脚H1,使用ICSP编程选项直接使用pickit3对PIC进行编程。


步骤2:在GPIO引脚上生成PWM信号以进行伺服电机控制

电路准备就绪后,我们必须弄清楚如何生成PWN PIC的GPIO引脚上的信号用于控制伺服电机。我们已经使用Timer中断方法累了类似的东西并且成功了。在这里,我们将建立在它之上。


所有业余爱好伺服电机的工作频率为50Hz。这意味着伺服电机的一个完整脉冲周期为1/50(F = 1/T),即20ms。在这20ms内,控制信号仅为0到2ms,而其余信号总是关闭。下图显示了ON时间如何仅在0到2ms之间变化,将电机从0度旋转到180度持续20ms。


考虑到这一点,我们必须在这样的情况下编写程序PIC从电位计读取0到1204的方式并将其映射到0到100,这将是伺服电机的占空比。使用此占空比,我们可以计算伺服电机的ON时间。然后我们可以定期中断初始化定时器中断,使其与Arduino中的millis()函数类似。这样,我们可以将状态GPIO引脚切换为高电平达到所需的持续时间,并在20ms(一个完整周期)后将其关闭,然后重复相同的过程。现在,我们已经了解了逻辑,让我们进入程序。


步骤3:为机器人臂编程PIC16F8771A

总是像完整的程序一样视频可以在本页末尾找到,代码也可以从这里下载所有必要的文件。在本节中,我们将讨论该程序背后的逻辑。该程序采用ADC模块,定时器模块和LCD模块来控制机械臂。如果您不了解如何使用ADC功能或定时器功能或将LCD与PIC连接,则可以回退到相应的链接以了解它们。假设读者熟悉这些概念,下面给出了解释。


定时器0端口配置

代码中最重要的部分是将Timer 0设置为过流具体延迟。计算此延迟的公式可以给出为

Delay = ((256-REG_val)*(Prescal*4))/Fosc

通过使用OPTION_REG和TMR0寄存器,我们将Timer 0设置为以预分频值32运行,REG val设置为248.我们硬件中使用的晶体频率(Fosc)为20Mhz。使用这些值,延迟可以计算为

Delay = ((256-248)*(32*4)) / (20000000)

= 0.0000512 seconds (or)

= 0.05 msec

所以现在我们已经将计时器设置为每0.05ms溢出一次。下面给出了执行相同操作的代码

/*****Port Configuration for Timer ******/

OPTION_REG = 0b00000100; // Timer0 with external freq and 32 as prescalar // Also Enables PULL UPs

TMR0=248; // Load the time value for 0.0001s; delayValue can be between 0-256 only

TMR0IE=1; //Enable timer interrupt bit in PIE1 register

GIE=1; //Enable Global Interrupt

PEIE=1; //Enable the Peripheral Interrupt

/***********______***********/

在伺服电机的总0ms到2ms控制窗口中,我们可以用0.05msec的分辨率控制它,这使得我们可以在0度之间为电机提供(2/0.05)40个不同的位置到180度。如果您的MCU可以支持它以获得更多位置和精确控制,您可以进一步降低此值。


中断服务程序(ISR)

现在我们将Timer 0设置为每0.05ms溢出一次,我们将TMR0IF中断标志设置为0.05ms。因此,在ISR函数中,我们可以重置该标志并将名为count的变量递增1。所以现在这个变量每0.05ms增加1。


void interrupt timer_isr() {

if(TMR0IF==1) // Timer flag has been triggered due to timer overflow -》 set to overflow for every 0.05ms {

TMR0 = 248; //Load the timer Value

TMR0IF=0; // Clear timer interrupt flag

count++; //Count increments by 1 for every 0.05ms }

计算占空比和导通时间

接下来,我们必须计算所有五个伺服电机的占空比和导通时间。我们有五个伺服电机,每个伺服电机用于控制臂的各个部分。因此,我们必须读取所有五个的ADC值,并计算每个的占空比和导通时间。


ADC值将在0到1024的范围内,只需将0.0976(100/1024 = 0.0976)乘以获得的值即可转换为0%至100%占空比。然后必须将此0到100%的占空比转换为ON时间。我们知道,在100%占空比时,ON时间必须为2ms(180度),因此乘以0.02(2/100 = 0.02)将0到100占空比转换为0到2ms。但是我们的定时器变量计数设置为每0.05ms增加一次。这意味着每1ms计数值将为20(1/0.05 = 20)。所以我们必须将20乘以0.02来计算我们程序的准确时间,这将给出0.4(0.02 * 20 = 0.4)的值。相同的代码如下所示,你可以看到它使用for循环重复5次。结果值存储在T_ON数组中。


for (int pot_num=0; pot_num《=3; pot_num++)

{ int Pev_val = T_ON[pot_num];

POT_val = (ADC_Read(pot_num)); //Read the value of POT using ADC

Duty_cycle = (POT_val * 0.0976); //Map 0 to 1024 to 0 to 100

T_ON[pot_num] = Duty_cycle* 0.4;//20*0.02


选择要旋转的电机

我们无法控制所有五个电机,因为它会使ISR代码大幅减速整个微控制器。所以我们一次只能旋转一个伺服电机。要选择旋转哪个伺服,微控制器会监控所有五个伺服电机的开启时间,并将其与之前的准时进行比较。如果ON时间发生变化,我们可以得出结论,必须移动特定的伺服。相同的代码如下所示。


if (T_ON[pot_num] != Pev_val) {

Lcd_Clear();

servo = pot_num;

Lcd_Set_Cursor(2,11); Lcd_Print_String(“S:”);

Lcd_Print_Char(servo+‘0’);

if (pot_num==0)

{Lcd_Set_Cursor(1,1);

Lcd_Print_String(“A:”);}

else if (pot_num==1)

{Lcd_Set_Cursor(1,6);

Lcd_Print_String(“B:”);}

else if (pot_num==2)

{Lcd_Set_Cursor(1,11);

Lcd_Print_String(“C:”);}

else if (pot_num==3)

{Lcd_Set_Cursor(2,1);

Lcd_Print_String(“D:”);}

else if (pot_num==4)

{Lcd_Set_Cursor(2,6);

Lcd_Print_String(“E:”);}

char d2 = (Duty_cycle) %10;

char d1 = (Duty_cycle/10) %10;

Lcd_Print_Char(d1+‘0’);Lcd_Print_Char(d2+‘0’);

我们还在LCD屏幕上打印伺服占空比,以便用户了解其当前位置。基于接通时间的变化,可变伺服系统用0到4的数字更新,每个数字代表各个电动机。


控制ISR内部的伺服电机

在ISR内部,变量计数每0.05ms增加一次,这意味着每1ms变量将增加20。使用它我们必须控制引脚以产生PWM信号。如果count的值小于导通时间,则使用下面的行

打开该电机的GPIOPORTD = PORTD | servo_code[servo];

此处,数组servo_code []具有所有五个伺服电机的引脚细节,并根据变量伺服中的值,将使用该特定伺服电机的代码。然后逻辑OR(|)与现有的PORTD位,这样我们就不会干扰其他电机的值,只更新这个特定的电机。类似地,关闭引脚

PORTD = PORTD & ~(servo_code[servo]);

我们使用逻辑反转(〜)运算符反转了位值,然后在PORTD上执行AND(&)操作以仅关闭所需的引脚,同时将其他引脚保持在先前的状态。完整的代码段如下所示。


void interrupt timer_isr() {

if(TMR0IF==1) // Timer flag has been triggered due to timer overflow -》 set to overflow for every 0.05ms {

TMR0 = 248; //Load the timer Value

TMR0IF=0; // Clear timer interrupt flag

count++; //Count increments by 1 for every 0.05ms -》 count will be 20 for every 1ms (0.05/1 = 20)) }

int servo_code[] = {0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100 };

if (count 》= 20*20)

count=0;

if (count 《= (T_ON[servo]) )

PORTD = PORTD | servo_code[servo];

else

PORTD = PORTD & ~(servo_code[servo]); }

我们知道在GPIO引脚再次打开之前,总周期必须持续20ms。因此,我们通过将count的值与400(上面讨论的计算相同)进行比较来检查计数是否超过20ms,如果是,我们必须再次将计数初始化为零。


步骤4:PIC机械臂代码的模拟

在将代码送到真实硬件之前,最好先模拟代码。所以我使用Proteus来模拟我的代码并验证它是否正常工作。用于仿真的电路如下所示。我们使用示波器检查是否按要求生成PWM信号。此外,我们可以验证LCD和伺服电机是否按预期旋转。


正如您所见,LCD根据第3电机的电位值显示电机D的占空比为07。类似的,如果移动另一个电位器,则该电位器的占空比及其电机编号将显示在LCD上。示波器上显示的PWM信号

使用示波器上的光标选项测量的总循环周期为22.2ms,非常接近所需的20ms。最后,我们确信代码有效,因此要继续使用电路,我们可以将其焊接在穿孔板上或使用PCB。它不会在面包板上轻松工作,因为POT总是会因连接不良而产生一些问题。


步骤5:使用EasyEDA进行PCB设计

设计此PIC机器人手臂,我们选择了名为EasyEDA的在线EDA工具。我已经使用它很长一段时间了,因为它占地面


关键字:PIC单片机  机械臂  制作教程 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机中断源的扩展方法