- تبدیل آنالوگ به دیجیتال (ADC) توسط LPC2148
برای تعیین مقدار Duty Cycle درمدولاسیون پهنای باند (PWM) ، جهت کنترل موقعیت سروو موتور لازم است از تبدیل آنالوگ به دیجیتال استفاده شود. از آنجا که چهار پتانسیومتر برای کنترل چهار سروو موتور در نظر گرفته شده، چهار کانال ADC میکروکنترلر LPC2148 مورد استفاده قرار میگیرد. در این پروژه از پینهای (P0.25, P0.28, P0.29, P0.30) یعنی به ترتیب کانالهای 4،1،2،3 در LPC2148 استفاده میشود.
- تولید سیگنال PWM برای سروو موتور با استفاده از LPC2148
برای کنترل موقعیت سروو موتور نیاز به سیگنالهای PWM است. در این مکانیزم چهار سروو موتور برای حرکت چهار مفصل بازو استفاده شده، بنابراین به چهار کانال PWM نیاز است که در اینجا از پینهای (P0.1, P0.7, P0.8, P0.21) یعنی به ترتیب کانالهای 5،4،2،3 در LPC2148 استفاده میشود.
- پیکربندی پورتهای LPC2148
از رجیسترPINSEL1 برای فعال کردن کانالهای ADC0.4 ، ADC0.1 ، ADC0.2 ، ADC0.3 یعنی پینهای P0.25 ، P0.28 ، P0.29 ، P0.30. استفاده میشود.
#define AD04 (1<<18) //Select AD0.4 function for P0.25
#define AD01 (1<<24) //Select AD0.1 function for P0.28
#define AD02 (1<<26) //Select AD0.2 function for P0.29
#define AD03 (1<<28) //Select AD0.3 function for P0.30
PINSEL1 |= AD04 | AD01 | AD02 | AD03 | (1<<10);
همچنین به منظور فعالسازی کانالهای PWM یعنی ( PWM3, PWM2, PWM4 ) از پینهای P0.1, P0.7, P0.8 استفاده میشود.
PINSEL0 = 0x000A800A;
توسط رجیستر PINSEL2، پینهای GPIO پورت یک، برای اتصال LEDها و شستیها فعال میشوند. برای تعریف نشانگرهای LED به عنوان پینهای خروجی و شستیها به عنوان ورودی، از رجیستر IODIR1 استفاده میشود. (0 برای ورودی و 1 برای خروجی)
IODIR1 = ((0<<17)|(0<<18)|(0<<19)|(0<<20)|(1<<28)|(1<<29)|(1<<30)|(1<<31));
شماره پینها نیز به صورت زیر تعریف می شود.
#define SwitchPinNumber1 17 // (Connected with P1.17)
#define SwitchPinNumber2 18 // (Connected with P1.18)
#define SwitchPinNumber3 19 // (Connected with P1.19)
#define SwitchPinNumber4 20 // (Connected with P1.20)
#define LedPinNumber1 28 // (Connected with P1.28)
#define LedPinNumber2 29 // (Connected with P1.29)
#define LedPinNumber3 30 // (Connected with P1.30)
#define LedPinNumber4 31 // (Connected with P1.31)
در این مرحله مود و کلاک ADC با استفاده از رجیستر AD0CR_setup تنظیم میشود.
unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP; //Setting up ADC Mode
#define CLKDIV (15-1)
#define BURST_MODE_OFF (0<<16) // 1 for on and 0 for off
#define PowerUP (1<<21)
- تنظیمات مدولاسیون پهنای پالس (PWM)
در گام اول با استفاده از رجیستر PWMTCR کانتر PWM را غیر فعال کرده و مقدار پیشتقسیم کنندهای (Prescaler) به تایمر PWM اختصاص داده میشود.
PWMTCR = 0x02;
PWMPR = 0x1D;
در این مرحله حداکثر تعداد شمارشها را در یک چرخه تنظیم میشود. این کار در رجیستر (Match 0 (PWMMR0 انجام میشود. مقدار 20000 برای تولید یک پالس 20 میلی ثانیهای در نظر گرفته میشود.
PWMMR0 = 20000;
پس از آن مقدار Duty Cycle برای PWMMR4 ، PWMMR2 ، PWMMR3 ، PWMMR5 مشخص میشود. در اینجا مقادیر اولیه 0 میلی ثانیه (Toff) تعیین شده است.
PWMMR4 = 0;
PWMMR2 = 0;
PWMMR3 = 0;
PWMMR5 = 0;
پس از آن رجیستر کنترلی PWM یعنی رجیستر Match ریست میشود.
PWMMCR = 0x00000002; // Reset on MR0 match
- انتخاب سروو موتورها جهت چرخش توسط شستیها
از چهار شستی برای چرخش چهار سروو موتور استفاده شده است. با انتخاب یک شستی تغییر میزان مقاومت پتانسیومتر مربوط به آن مقدار Duty Cycle تعیین شده و سروو موتور مربوطه موقعیت خود را تغییر میدهد. برای به دست آوردن وضعیت شستی داریم:
switchStatus1 = (IOPIN1>>SwitchPinNumber1) & 0x01 ;
بنابراین بسته به اینکه مقدار شستی High باشد، تبدیل ADC صورت گرفته و پس از تبدیل موفقیتآمیز مقدار ADC از بازهی (0 تا 1023) به بازهی (0 تا 2045) نگاشت شده و سپس مقدار Duty Cycle در پین PWM متصل به سروو موتور (PWMMRx) نوشته میشود. همچنین یک نشانگرLED همزمان با High شدن شستی روشن میشود.
if(switchStatus1 == 1)
{
IOPIN1 = (1<<LedPinNumber1); //Make LED1 HIGH
AD0CR = AD0CR_setup | SEL_AD01; //Setup ADC for channel 1
AD0CR |= START_NOW; //Start new Conversion at ADC1
while( (AD0DR1 & ADC_DONE) == 0 ) //Check for ADC conversion
{
convert1 = (AD0DR1>>6) & 0x3ff; //Get ADC value
result1 = map(convert1,0,1023,0,2450); //Convert ADC values in terms of dutycyle for PWM
PWMMR5 = result1; //Set Duty Cylce value to PWM5
PWMLER = 0x20; //Enable PWM5
delay_ms(2);
}
}
else
{
IOPIN1 = (0<<LedPinNumber1); //Set LED1 LOW
}
- کارکرد بازوی رباتیکی Pick and Place
پس از آپلود برنامه در LPC2148 یک شستی را فشار داده و پتانسیومتر مربوطه را تغییر دهید تا موقعیت بازوی ربات تغییر کند. هر شستی و پتانسیومتر حرکت سروو موتور را برای حرکت به سمت بالا و پایین، جلو و عقب و یا برای باز و بسته شدن گریپر کنترل میکند.
سورس کد و فیلم کوتاهی از پروژه در زیر قرار داده شده است.
#include <lpc214x.h>
#include <stdint.h>
#define SwitchPinNumber1 17
#define SwitchPinNumber2 18
#define SwitchPinNumber3 19
#define SwitchPinNumber4 20
#define LedPinNumber1 28
#define LedPinNumber2 29
#define LedPinNumber3 30
#define LedPinNumber4 31
#define AD04 (1<<18) //Select AD0.4 function for P0.25
#define AD01 (1<<24) //Select AD0.1 function for P0.28
#define AD02 (1<<26) //Select AD0.2 function for P0.29
#define AD03 (1<<28) //Select AD0.3 function for P0.30
#define SEL_AD04 (1<<4) //Select ADC channel 4
#define SEL_AD01 (1<<1) //Select ADC channel 1
#define SEL_AD02 (1<<2) //Select ADC channel 2
#define SEL_AD03 (1<<3) //Select ADC channel 3
#define CLKDIV (15-1) // 4Mhz ADC clock (ADC_CLOCK=PCLK/CLKDIV) where "CLKDIV-1" is actually used , in our case PCLK=60mhz
#define BURST_MODE_OFF (0<<16) // 1 for on and 0 for off
#define PowerUP (1<<21)
#define START_NOW ((0<<26)|(0<<25)|(1<<24)) //001 for starting the conversion immediately
#define ADC_DONE (1UL<<31)
#define VREF 3.3 //Reference Voltage at VREF Pin
volatile int result1=0;
volatile int result2=0;
volatile int result3=0;
volatile int result4=0;
volatile int convert1=0;
volatile int convert2=0;
volatile int convert3=0;
volatile int convert4=0;
volatile unsigned int switchStatus1;
volatile unsigned int switchStatus2;
volatile unsigned int switchStatus3;
volatile unsigned int switchStatus4;
long map(long x, long in_min, long in_max, long out_min, long out_max) // Map function to convert range
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void delay_ms(uint16_t j) /* loop to generate 1 milisecond delay with Cclk = 60MHz */
{
uint16_t x,i;
for(i=0;i<j;i++)
{
for(x=0; x<6000; x++);
}
}
int main()
{
PINSEL0 = 0x000A800A; // Configure P0.1,P0.7,P0.8 as PWM3,PWM2,PWM4 pins
PINSEL2 = 0x00000000; //Configure the PORT1 Pins as GPIO;
PINSEL1 |= AD04 | AD01 | AD02 | AD03 | (1<<10); // Configure P0.25,P0.28,P0.29,P0.30 as ADC0.4, ADC0.1, ADC0.2, ADC0.3 Pins & P0.21 as PWM5
IODIR1 = ((0<<17)|(0<<18)|(0<<19)|(0<<20)|(1<<28)|(1<<29)|(1<<30)|(1<<31)); //Configure LED pin as output and Switch Pin as input
unsigned long AD0CR_setup = (CLKDIV<<8) | BURST_MODE_OFF | PowerUP; //Setting up ADC Mode
PWMTCR = 0x02; // Reset and disable counter for PWM
PWMPR = 0x1D; // Prescale Register value
PWMMR0 = 20000; // Time period of PWM wave, 20msec
PWMMR4 = 0; // Initial values of PWM wave 0 msec
PWMMR2 = 0;
PWMMR3 = 0;
PWMMR5 = 0;
PWMMCR = 0x00000002; // Reset on MR0 match
PWMLER = 0x7C; // Latch enable for PWM2,PWM4,PWM4 and PWM5
PWMPCR = 0x7C00; // Enable PWM2,PWM4,PWM4 and PWM5, single edge controlled PWM
PWMTCR = 0x09; // Enable PWM and counter
while(1)
{
switchStatus1 = (IOPIN1>>SwitchPinNumber1) & 0x01 ; // Read the switch status
switchStatus2 = (IOPIN1>>SwitchPinNumber2) & 0x01 ; // Read the switch status
switchStatus3 = (IOPIN1>>SwitchPinNumber3) & 0x01 ; // Read the switch status
switchStatus4 = (IOPIN1>>SwitchPinNumber4) & 0x01 ; // Read the switch status
if(switchStatus1 == 1) //Turn ON/OFF LEDs depending on switch status and start ADC conversion
{
IOPIN1 = (1<<LedPinNumber1); //Make LED1 HIGH
AD0CR = AD0CR_setup | SEL_AD01; //Setup ADC for channel 1
AD0CR |= START_NOW; //Start new Conversion at ADC1
while( (AD0DR1 & ADC_DONE) == 0 ) //Check for ADC conversion
{
convert1 = (AD0DR1>>6) & 0x3ff; //Get ADC value
result1 = map(convert1,0,1023,0,2450); //Convert ADC values in terms of dutycyle for PWM
PWMMR5 = result1; //Set Duty Cylce value to PWM5
PWMLER = 0x20; //Enable PWM5
delay_ms(2);
}
}
else
{
IOPIN1 = (0<<LedPinNumber1); //Set LED1 LOW
}
if(switchStatus2 == 1) //Turn ON/OFF LEDs depending on switch status and start ADC conversion
{
IOPIN1 = (1<<LedPinNumber2); //Make LED2 HIGH
AD0CR = AD0CR_setup | SEL_AD02; //Setup ADC for channel 2
AD0CR |= START_NOW; //Start new Conversion at ADC2
while( (AD0DR2 & ADC_DONE) == 0 ) //Check for ADC conversion
{
convert2 = (AD0DR2>>6) & 0x3ff; //Get ADC value
result2 = map(convert2,0,1023,0,2450); //Convert ADC values in terms of dutycyle for PWM
PWMMR4 = result2; //Set Duty Cylce value to PWM4
PWMLER = 0x10; //Enable PWM4
delay_ms(2);
}
} else
{
IOPIN1 = (0<<LedPinNumber2); //Set LED2 LOW
}
if(switchStatus3 == 1) //Turn ON/OFF LEDs depending on switch status and start ADC conversion
{
IOPIN1 = (1<<LedPinNumber3);
//Set LED3 HIGH
AD0CR = AD0CR_setup | SEL_AD03;
//Setup ADC for channel 3
AD0CR |= START_NOW; //Start new Conversion at ADC3
while( (AD0DR3 & ADC_DONE) == 0 ) //Check for ADC conversion
{
convert3 = (AD0DR3>>6) & 0x3ff; //Get ADC value
result3 = map(convert3,0,1023,0,2450); //Convert ADC values in terms of dutycyle for PWM
PWMMR3 = result3; //Set Duty Cylce value to PWM3
PWMLER = 0x08; //Enable PWM3
delay_ms(2);
}
} else
{
IOPIN1 = (0<<LedPinNumber3); //Set LED4 LOW
}
if(switchStatus4 == 1) //Turn ON/OFF LEDs depending on switch status and start ADC conversion
{
IOPIN1 = (1<<LedPinNumber4); //Set LED4 HIGH
AD0CR = AD0CR_setup | SEL_AD04; //Setup ADC for channel 4
AD0CR |= START_NOW; //Start new Conversion at ADC4
while( (AD0DR4 & ADC_DONE) == 0 ) //Check for ADC conversion
{
convert4 = (AD0DR4>>6) & 0x3ff; //Get ADC value
result4 = map(convert4,0,1023,0,2450); //Convert ADC values in terms of dutycyle for PWM
PWMMR2 = result4; //Set Duty Cylce value to PWM
PWMLER = 0x04; //Enable PWM2
delay_ms(2);
}
} else
{
IOPIN1 = (0<<LedPinNumber4); //Set LED4 LOW
}
}
}
Video