- Vad är en PWM-signal?
- Programmerar PIC för att generera PWM på GPIO Pins
- Kretsschema
- Simulering
- Hårdvaruinställning för styrning av servomotor med PIC Microcontroller
PWM-signalgenerering är ett viktigt verktyg i varje inbäddad ingenjörsarsenal, de är mycket användbara för många applikationer som att styra servomotorns position, växla några elektroniska IC-kretsar i omvandlare / inverterare och till och med för en enkel LED-ljusstyrningskontroll. I PIC-mikrokontroller kan PWM-signaler genereras med modulerna Jämför, Fånga och PWM (CCP) genom att ställa in nödvändiga register, vi har redan lärt oss hur man gör det i PIC PWM-handledning. Men det finns en betydande nackdel med den metoden.
Den PIC16F877A kan generera PWM-signaler endast på stift RC1 och RC2, om vi använder KKP moduler. Men vi kan stöta på situationer där vi behöver fler stift för att ha PWM-funktionalitet. Till exempel i mitt fall vill jag styra 6 RC-servomotorer för mitt robotarmprojekt för vilket CCP-modulen är hopplös. I dessa scenarier kan vi programmera GPIO-stiften så att de producerar PWM-signaler med hjälp av tidsmoduler. På så sätt kan vi generera så många PWM-signaler med vilken stift som helst. Det finns också andra hårdvaruhackar som att använda en multiplexer-IC, men varför investera på hårdvara när samma kan uppnås genom programmering. Så i den här handledningen lär vi oss hur man konverterar en PIC GPIO-stift till en PWM-stift och för att testa den simulerar vi den på proteus med digitalt oscilloskop och ävenkontrollera servomotorns läge med hjälp av PWM-signalen och ändra dess arbetscykel genom att variera en potentiometer.
Vad är en PWM-signal?
Innan vi går in i detaljerna, låt oss pussa lite på vad PWM-signaler är. Pulsbreddsmodulering (PWM) är en digital signal som oftast används i styrkretsar. Denna signal är inställd hög (5v) och låg (0v) på en fördefinierad tid och hastighet. Tiden under vilken signalen förblir hög kallas "i tid" och den tid under vilken signalen förblir låg kallas "avstängningstid". Det finns två viktiga parametrar för en PWM som diskuteras nedan:
PWM: s arbetscykel
Procentandelen tid under vilken PWM-signalen förblir HÖG (i tid) kallas som arbetscykel. Om signalen alltid är PÅ är den i 100% arbetscykel och om den alltid är av är den 0% arbetscykel.
Driftscykel = Slå PÅ-tid / (Slå PÅ-tid + Stäng av tid)
Variabelt namn |
Refererar till |
PWM_Frekvens |
PWM-signalens frekvens |
T_TOTAL |
Total tid som tagits för en fullständig PWM-cykel |
TON |
Vid tidpunkten för PWM-signalen |
T_OFF |
PWM-signalens avstängningstid |
Duty_cykel |
PWM-signalens arbetscykel |
Så nu, låt oss göra matte.
Detta är standardformlerna där frekvensen helt enkelt är den ömsesidiga tiden. Frekvensvärdet måste bestämmas och ställas in av användaren baserat på hans / hennes applikationskrav.
T_TOTAL = (1 / PWM_Frekvens)
När användaren ändrar arbetscykelvärdet bör vårt program automatiskt justera T_ON-tiden och T_OFF-tiden enligt det. Så ovanstående formler kan användas för att beräkna T_ON baserat på värdet av Duty_Cycle och T_TOTAL.
T_ON = (Duty_Cycle * T_TOTAL) / 100
Eftersom den totala tiden för PWM-signalen för en hel cykel är summan av tid och avstängningstid. Vi kan beräkna avstängningstiden T_OFF som visas ovan.
T_OFF = T_TOTAL - T_ON
Med dessa formler i åtanke kan vi börja programmera PIC-mikrokontrollern. Programmet involverar PIC-timermodulen och PIC ADC-modulen för att skapa en PWM-signal baserad med en varierande arbetscykel enligt ADC-värdet från POT. Om du inte har använt dessa moduler så rekommenderas det starkt att du läser lämplig handledning genom att klicka på hyperlänkarna.
Programmerar PIC för att generera PWM på GPIO Pins
Det fullständiga programmet för denna handledning finns som längst ner på webbplatsen. I det här avsnittet ska vi förstå hur programmet egentligen är skrivet. Som alla program börjar vi med att ställa in konfigurationsbitarna. Jag har använt minnesvyalternativet för att ställa in det för mig.
// CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT inaktiverad) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled ) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 är digital I / O, HV på MCLR måste användas för programmering) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; allt programminne kan skrivas till av EEGON-kontroll) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) // #pragma config statement bör föregå att projektfilen innehåller. // Använd projekt-enums istället för #define för ON och OFF. #omfatta
Sedan nämner vi klockfrekvensen som används i hårdvaran, här använder min hårdvara 20MHz kristall, du kan ange värdet baserat på din hårdvara. Följt av detta är frekvensvärdet för PWM-signalen. Eftersom mitt mål här är det att styra en hobby RC servomotor som kräver en PWM-frekvens på 50Hz har jag ställt in 0,05 KHz som frekvensvärde. Du kan också ändra detta baserat på dina applikationskrav.
#define _XTAL_FREQ 20000000 #define PWM_Frequency 0.05 // in KHz (50Hz)
Nu när vi har värdet av Frekvens kan vi beräkna T_TOTAL med de ovan diskuterade formlerna. Resultatet dykas med 10 för att få värdet av tiden i millisekunder. I mitt fall kommer värdet T_TOTAL att vara 2 millisekunder.
int T_TOTAL = (1 / PWM_Frekvens) / 10; // beräkna Total tid från frekvens (i millisekunder)) // 2msec
Därefter initialiserar vi ADC-modulerna för att läsa positionen för potentiometern som diskuteras i vår ADC PIC-handledning. Därefter har vi avbrottsrutinen som kommer att anropas varje gång, timern rinner över vi kommer tillbaka till detta senare, för nu ska vi kolla huvudfunktionen.
Inuti huvudfunktionen konfigurerar vi timer-modulen. Här har jag konfigurerat Timer-modulen för att flöda för varje 0,1 ms. Värdet för tiden kan beräknas med hjälp av formlerna nedan
RegValue = 256 - ((Delay * Fosc) / (Prescalar * 4)) fördröjning i sekunder och Fosc in hz
I mitt fall för en fördröjning på 0,0001 sekunder (0,1 ms) med prescalar på 64 och Fosc på 20 MHz bör värdet på mitt register (TMR0) vara 248. Så konfigurationen ser ut så här
/ ***** Portkonfiguration för timer ****** / OPTION_REG = 0b00000101; // Timer0 med extern freq och 64 som prescalar // Aktiverar även PULL UPs TMR0 = 248; // Ladda tidsvärdet för 0,0001s; delayValue kan vara mellan 0-256 endast TMR0IE = 1; // Aktivera timeravbrottsbit i PIE1-registret GIE = 1; // Aktivera Global Interrupt PEIE = 1; // Aktivera perifert avbrott / *********** ______ *********** /
Då måste vi ställa in in- och utmatningskonfigurationen. Här använder vi AN0-stiftet för att läsa ADC-värdet och PORTD-stiften för att mata ut PWM-signalerna. Så initiera dem som utgångsstift och gör dem låga genom att använda nedanstående kodrader.
/ ***** Portkonfiguration för I / O ****** / TRISD = 0x00; // Instruera MCU att alla stift på PORT D matas ut PORTD = 0x00; // Initiera alla stift till 0 / *********** ______ *********** /
Inne i oändliga medan loop måste vi beräkna värdet av tid (T_On) från driftcykeln. Den tid och duty cykel varierar beroende på placeringen av POT så vi gör det upprepade gånger innanför medan slingan enligt nedan. 0,0976 är det värde som måste multipliceras med 1024 för att få 100 och för att beräkna T_ON har vi multiplicerat det med 10 för att få värde i millisekunder.
medan (1) { POT_val = (ADC_Read (0)); // Läs värdet på POT med ADC Duty_cycle = (POT_val * 0,0976); // Karta 0 till 1024 till 0 till 100 T_ON = ((Duty_cycle * T_TOTAL) * 10/100); // Beräkna tid med hjälp av formelenheten i millisekunder __fördröjning_ms (100); }
Eftersom timern är inställd på överflöde för varje 0,1 ms kommer tidsavbrottstjänstrutinen ISR att anropas för varje 0,1 ms. Inuti service rutinen använder vi en variabel som kallas count och ökar den för varje 0,1 ms. På så sätt kan vi hålla koll på tiden. För att lära dig mer om avbrott i PIC-mikrokontroller, följ länkarna
om (TMR0IF == 1) // Timerflagga har utlösts på grund av timeröverflöde -> inställt på överflöd för varje 0,1 ms { TMR0 = 248; // Ladda timern Värde TMR0IF = 0; // Rensa timer avbryta flaggantal ++; // Räkna steg för varje 0,1 ms -> räkna / 10 ger värdet av räkningen i ms }
Slutligen är det dags att växla GPIO-stift baserat på värdet på T_ON och T_OFF. Vi har räknevariabeln som håller reda på tiden i millisekunder. Så vi använder den variabeln för att kontrollera om tiden är mindre än i tid , om ja, så håller vi GPIO-stiftet påslaget, annars stänger vi av det och håller det avstängt tills den nya cykeln börjar. Detta kan göras genom att jämföra det med den totala tiden för en PWM-cykel. Koden för att göra detsamma visas nedan
if (count <= (T_ON)) // If time less than on time RD1 = 1; // Slå på GPIO annars RD1 = 0; // Stäng annars av GPIO om (count> = (T_TOTAL * 10)) // Håll den avstängd tills en ny cykel startar count = 0;
Kretsschema
Kretsschemat för att generera PWM med GPIO-stift av PIC-mikrokontroller är väldigt enkelt, bara driva PIC med oscillator och anslut potentiometern till stift AN0 och Servomotor för stift RD1, vi kan använda GPIO-stift för att få PWM-signalen, jag har valt RD1 helt slumpmässigt. Både potentiometern och servomotorn drivs av 5V som regleras från 7805 som visas nedan i kretsschemat.
Simulering
För att simulera projektet använde jag min proteus-programvara. Bygg kretsen som visas nedan och länka koden till din simulering och kör den. Du bör få en PWM-signal på RD1 GPIO-stiftet enligt vårt program och PWM: s arbetscykel bör kontrolleras baserat på potentiometerns position. Nedanstående GIF visar hur PWM-signalen och servomotorn svarar när ADC-värdet ändras genom potentiometern.
Hårdvaruinställning för styrning av servomotor med PIC Microcontroller
Min kompletta hårdvaruuppsättning visas nedan, för personer som följer mina självstudier borde detta bord se bekant ut, det är samma kort som jag har använt i alla mina självstudier hittills. Du kan hänvisa till Blinking LED tutorial om du är intresserad av att veta hur jag bygger den. Annars är det bara att följa kretsschemat ovan och allt ska fungera bra.
Ladda upp programmet och variera potentiometern så ser du att servon ändrar position baserat på potentiometerns position. Det fullständiga arbetet med projektet visas i videon i slutet av denna sida. Hoppas att du förstod projektet och tyckte om att bygga, om du har frågor, lägg gärna in dem på forumet så ska jag göra mitt bästa för att svara.
Jag planerar att ta detta projekt framåt genom att lägga till alternativ för att styra flera servomotorer och därmed bygga en robotarm ur den, liknande den Arduino Robotic Arm som vi redan har byggt. Så tills dess, se dig !!