![]() | ![]() | ![]() |
| |||||||
| Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc. |
![]() |
| | Tools |
| | #1 |
|
Hello, I'm building a cheapo line follower robot with pic10F200 as its brains. At the moment I am turning motors on and off when I need to turn, but that results in a jerky movement. I want to go with PWM for smooth ride. I have managed to find some examples of software PWM online, but there are two major flaws with them: 1 - they are for bigger and more powerful chips with multiple timers 2 - the PWM cycle is not easily modified "on-the-run" Has anyone done soft PWM for small chips like that?
__________________ Here...let me improve it for you! *Puff of smoke* ....oops... | |
| |
| | #2 |
|
You could do software PWM on the 10F200 but it would be difficult. No interrupts, only timer 0 and 256 instructions is very limiting. How is your code arranged at the moment, how nested are your calls and what sensors are you reading? Mike. I am just on my way out but will look for a reply tomorrow. | |
| |
| | #3 |
|
At the moment the code is very simple, I have two inputs from two IR sensors following the line. The sensors are not on the line when running, they are on either side of it, so if a sensor sees the line, the bot turns in that direction until it does not see the line anymore. basically: ----loop---- is RightSensor on line? Yes, stop Rightmotor No, run Rightmotor is LeftSensor on line? Yes, stop Leftmotor No, run Leftmotor ----loop----
__________________ Here...let me improve it for you! *Puff of smoke* ....oops... | |
| |
| | #4 |
|
Here is a bit of code you may find useful. ;FADES THREE LEDS IN SEQUENCE AT ABOUT ONE HERTZ WHEN ENABLE IS APPLIED ; #INCLUDE <P10F200.INC> ; PROCESSOR SPECIFIC VARIABLES __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC ORG 0X1FF CBLOCK .16 COUNT ;COUNT BUFFER COUNT1 ;COUNT BUFFER #1 PWM_BUF ;PWM BUFFER ENDC ORG .0 ; MOVWF OSCCAL ; LOADS THE OSCILLATOR CALIBRATION FACTOR AUTOMATICALY. INIT ;-----[OPTION]--------------------------------------------------------------- ; 7 = GLOBAL WAKE UP ON CHANGE - GP0,GP1,GP3 - 1=ON 0=OFF ; 6 = GLOBAL WEAK PULL UP - GP0,GP1,GP3 - 1=ON 0=OFF ; 5 = T0CS - TIMER 0 CLOCK SOURCE SELECT - 1=EXTERNAL 0=INTERNAL ; 4 = T0SE - TIMER 0 SOURCE EDGE SELECT - 1=HIGH TO LOW 0=LOW TO HIGH ; 3 = PSA - PRE SCALE ASSIGNMENT - 1=WDT 0=TIMER 0 ; 2:1 = PRE SCALER RATE MOVLW B'10000111' ; SET PULL-UPS ONLY, MAX PRE-SCALER OPTION ; ;-----[TRIS]----------------------------------------------------------------- ; 3 = GPIO 3 - INPUT ONLY ; 2 = GPIO 2 - I/O ; 1 = GPIO 1 - I/O ; 0 = GPIO 0 - I/O MOVLW B'00001000' ; SET GPIO PIN 3 TO INPUT, PINS 0,1,2 TO OUTPUT. TRIS GPIO ; ;-----[CMCON0 FOR 10F204 AND 10F206 ONLY]------------------------------------- ; 7 = CMPOUT - 1=VIN+ > VIN : 0=VIN+ < VIN ; 6 = COUTEN - OUTPUT PLACED ON COMPARATOR PIN 1=YES : 0=NO ; 5 = POL - COMPARATOR OUTPUT BIT IS INVERTED 1=NOT : 0=IS ; 4 = CMPT0CS - 1=TMR0 SOURCE BY T0CKS BIT : 0=COMP OUT USED AS SOURCE ; 3 = CMPON - 1=COMPARATOR ON : 0=COMPARATOR OFF ; 2 = CNREF - NEGATIVE REF SELECT 1=CIN : 0=INTERNAL VOLTAGE ; 1 = CPREF - POSITIVE REF SELECT 1=CIN+ :0= CIN- ; 0 = CWU - COMPARATOR WAKE UP ON CHANGE 1=DISABLED 0=ENABLED ;-----[MAIN ROUTINE]--------------------------------------------------------- MAIN BCF GPIO,0 ; START WITH LED TURNED OFF BCF GPIO,1 ; START WITH LED TURNED OFF BCF GPIO,2 ; BTFSC GPIO,3 ; PRESS THE PUSHBUTTON TO BEGIN ; GOTO MAIN ; ...LOOP, IF THE PUSHBUTTON IS NOT PRESSED CLRF COUNT ; THE REG COUNT CAUSES THE SUBROUTINE TO LOOP 255 TIMES. CLRF COUNT1 ; THE REG COUNT1 SETS THE PWM VALUE CLRF PWM_BUF ; THE REG PWM_BUF HOLDS THE PWM RATIO OF 'ON' TO 'OFF' MAIN1 MOVF COUNT1,W ; TRANSFER THE VALUE IN REG COUNT1 TO REG PWM_BUF MOVWF PWM_BUF CALL PWM INCFSZ COUNT1,F ; HAS COUNT1 COUNTED FROM 0 TO 255? GOTO MAIN1 ; ...NO, LOOP AGAIN MAIN2 DECF COUNT1,F ; ...YES, AND NOW COUNT1 HOLDS THE DECREMENTING PWM VALUE MAIN3 MOVF COUNT1,W ; TRANSFER THE VALUE IN REG COUNT1 TO REG PWM_BUF MOVWF PWM_BUF CALL PWMA DECFSZ COUNT1,F ; HAS COUNT1 COUNTED FROM 255 TO 0? GOTO MAIN3 ; ...NO, LOOP AGAIN MAIN4 MOVF COUNT1,W ; TRANSFER THE VALUE IN REG COUNT1 TO REG PWM_BUF MOVWF PWM_BUF CALL PWMB INCFSZ COUNT1,F ; HAS COUNT1 COUNTED FROM 0 TO 255? GOTO MAIN4 ; ...NO, LOOP AGAIN MAIN5 DECF COUNT1,F ; ...YES, AND NOW COUNT1 HOLDS THE DECREMENTING PWM VALUE MAIN6 MOVF COUNT1,W ; TRANSFER THE VALUE IN REG COUNT1 TO REG PWM_BUF MOVWF PWM_BUF CALL PWMC DECFSZ COUNT1,F ; HAS COUNT1 COUNTED FROM 255 TO 0? GOTO MAIN6 ; ...NO, LOOP AGAIN GOTO MAIN PWM MOVLW .255 ; MOVE THE LITERAL COUNT OF 255 TO REG 'W' MOVWF COUNT ; ...THEN STORE IT IN THE REGISTER 'COUNT' PWM1 MOVF PWM_BUF,F ; TEST IF THE REGISTER PWM_BUF IS ZERO BTFSC STATUS,Z ; ...IS PWM_BUF = 0? GOTO LED_OFF ; ...NO, NOT YET, SKIP THIS INSTRUCTION FOR NOW. LED_ON BSF GPIO,0 ; ...YES, SET GPIO PIN-0 HIGH, TURN ON LED BCF GPIO,1 ; ...YES, SET GPIO PIN-1 LOW, TURN OFF LED DECF PWM_BUF,F ; DECREMENT THE REGISTER PWM_BUF GOTO EXIT LED_OFF BCF GPIO,0 ; SET GPIO PIN-0 LOW, TURN-OFF LED BSF GPIO,1 ; SET GPIO PIN-1 HIGH, TURN-ON LED NOP GOTO EXIT ; EXIT DECFSZ COUNT,F ; HAS THIS SUBROUTINE LOOPED 255 TIMES? GOTO PWM1 ; ...NO, KEEP LOOPING RETLW 0 ; ...YES, RETURN TO THE CALLING PROGRAM PWMA MOVLW .255 ; MOVE THE LITERAL COUNT OF 255 TO REG 'W' MOVWF COUNT ; ...THEN STORE IT IN THE REGISTER 'COUNT' PWM1A MOVF PWM_BUF,F ; TEST IF THE REGISTER PWM_BUF IS ZERO BTFSC STATUS,Z ; ...IS PWM_BUF = 0? GOTO LED_OFFA ; ...NO, NOT YET, SKIP THIS INSTRUCTION FOR NOW. LED_ONA BSF GPIO,0 ; ...YES, SET GPIO PIN-0 HIGH, TURN ON LED BCF GPIO,2 ; ...YES, SET GPIO PIN-2 LOW, TURN OFF LED DECF PWM_BUF,F ; DECREMENT THE REGISTER PWM_BUF GOTO EXITA LED_OFFA BCF GPIO,0 ; SET GPIO PIN-0 LOW, TURN-OFF LED BSF GPIO,2 ; SET GPIO PIN-2 HIGH, TURN-ON LED NOP GOTO EXITA ; EXITA DECFSZ COUNT,F ; HAS THIS SUBROUTINE LOOPED 255 TIMES? GOTO PWM1A ; ...NO, KEEP LOOPING RETLW 0 ; ...YES, RETURN TO THE CALLING PROGRAM PWMB MOVLW .255 ; MOVE THE LITERAL COUNT OF 255 TO REG 'W' MOVWF COUNT ; ...THEN STORE IT IN THE REGISTER 'COUNT' PWM1B MOVF PWM_BUF,F ; TEST IF THE REGISTER PWM_BUF IS ZERO BTFSC STATUS,Z ; ...IS PWM_BUF = 0? GOTO LED_OFFB ; ...NO, NOT YET, SKIP THIS INSTRUCTION FOR NOW. LED_ONB BSF GPIO,0 ; ...YES, SET GPIO PIN-0 HIGH, TURN ON LED BCF GPIO,2 ; ...YES, SET GPIO PIN-2 LOW, TURN OFF LED DECF PWM_BUF,F ; DECREMENT THE REGISTER PWM_BUF GOTO EXITB LED_OFFB BCF GPIO,0 ; SET GPIO PIN-0 LOW, TURN-OFF LED BSF GPIO,2 ; SET GPIO PIN-2 HIGH, TURN-ON LED NOP GOTO EXITB ; EXITB DECFSZ COUNT,F ; HAS THIS SUBROUTINE LOOPED 255 TIMES? GOTO PWM1B ; ...NO, KEEP LOOPING RETLW 0 ; ...YES, RETURN TO THE CALLING PROGRAM PWMC MOVLW .255 ; MOVE THE LITERAL COUNT OF 255 TO REG 'W' MOVWF COUNT ; ...THEN STORE IT IN THE REGISTER 'COUNT' PWM1C MOVF PWM_BUF,F ; TEST IF THE REGISTER PWM_BUF IS ZERO BTFSC STATUS,Z ; ...IS PWM_BUF = 0? GOTO LED_OFFC ; ...NO, NOT YET, SKIP THIS INSTRUCTION FOR NOW. LED_ONC BSF GPIO,0 ; ...YES, SET GPIO PIN-O HIGH, TURN ON LED BCF GPIO,1 ; ...YES, SET GPIO PIN-1 LOW, TURN OFF LED DECF PWM_BUF,F ; DECREMENT THE REGISTER PWM_BUF GOTO EXITC LED_OFFC BCF GPIO,0 ; SET GPIO PIN-O LOW, TURN-OFF LED BSF GPIO,1 ; SET GPIO PIN-1 HIGH, TURN-ON LED NOP GOTO EXITC ; EXITC DECFSZ COUNT,F ; HAS THIS SUBROUTINE LOOPED 255 TIMES? GOTO PWM1C ; ...NO, KEEP LOOPING RETLW 0 ; ...YES, RETURN TO THE CALLING PROGRAM END ; | |
| |
| | #5 |
|
Here is what I use. dutyc_msb and dutyc_lsb are the duty cycle. This gives you 12 bit duty cycle resolution, so the bottom 4 bits of dutyc_lsb are ignored. The PWM period is 4096 cycles or about 4ms and the resolution is 1 cycle. Your code has to change dutyc_msb and dutyc_lsb to what you want. count1 and count2 are temporary registers. out_dc_port, out_dc is the port that the duty cycle appears on. As others have pointed out, the 10f200 is not a powerful processor. The program has to output the mark space ratio, so it has to spend a lot of time waiting for the timer. At the point marked "run" is where you insert your code. The part that I have written always goes to "run" at the start of the larger half of the duty cycle. If the duty cycle is 25% it will go to "run" as soon as the output falls, but if the duty cycle is 60% it will go to "run" as soon as the output rises. In other words, your code can take up to about 2000 instruction cycles, and it will be run every 4 ms. Code: movlw b'11000011'
option ;set timer to prescale of 16
chk_1
movf dutyc_msb, w ;collect time
subwf timer,w ;subtract, result in w
btfsc status, carry ;see if time is less than w (dutyc_msb)
goto chk_1 ; If it is, output a 1
;we now want to wait until the next increment of the timer,
;trap the time exactly,
;In this direction, timer=0 here, so we are trying to find when it
;hits 1
nop ;timer now at 0, wait a bit
movlw 0x02
movwf count1
wait
decfsz count1,f
goto wait ;short pause needed
clrw ; This is the minimum number of steps
addwf timer,w ;1st place timer hits 1
addwf timer,w ;2nd
addwf timer,w ;3rd
addwf timer,w ;4th
;w is now between 0 and 4
andlw 0x07 ;shouldn't be needed
addwf pc, f ;increment PC restore timing
nop
nop
nop
nop
movlw 0x05
movwf count1
wait2
decfsz count1,f
goto wait2
nop ;These get the mark space ratio equal to dutyc
bsf out_dc_port, out_dc ;output 1
;the calulation done if duty cycle is below half
btfsc dutyc_msb, 7
goto run
; Output is high when chk_0 is running.
; dutyc_msb>time, so w>timer at the subtract
; so carry is not set
; When time equals result_time, w<=timer at the subract
; so carry is set and 0 is output
chk_0
movf dutyc_msb, w ;collect time
subwf timer,w ;subtract, result in w
btfss status, carry ;see if time is >= than w (dutyc_msb)
goto chk_0
;we now want to wait until the next increment of the timer,
;trap the time exactly,
;In this direction, timer=0 here, so we are trying to find when it
;hits 1
swapf dutyc_lsb,w ; collect the lsb word, biggest half
movwf count1 ; put in count 1
clrf count2 ;
subwf count2,f ; get the negative
movlw 0x03 ;
andwf count2,f ; bits 0 and 1 only.
;This part must be 6 cycles long exactly
;to be ready for the following bit
clrw
btfsc dutyc_msb,0 ;
iorlw 0x04
;This is so that we get the same last 3 bits after the additions
addwf timer,w ;1st place timer hits 1
addwf timer,w ;2nd
addwf timer,w ;3rd
addwf timer,w ;4th
andlw 0x07 ;take last 3 bit only
;w is now between 0 and 4
addwf pc, f ;increment PC restore timing
nop
nop
nop
nop ;this has to be here for restoring timing
rrf count1,f ;
rrf count1,f ;
movlw 0x03 ;
andwf count1,f ;
incf count1, f
time1
nop
decfsz count1,f ;
goto time1 ;This is to add cycles for bits 8 and 9
;of duty cycle. Each count of bit 9 represents 4 cycles
swapf dutyc_lsb,w ;This adds the next two bits of dutyc_lsb
movwf count1 ;bits 4 and 5
comf count1,f
movlw 0x03
andwf count1,w
addwf pc,f
nop
nop
nop
bcf out_dc_port, out_dc ;output 0
;this happens dutyc_lsb (bits 4 - 7 only)
;clock cycles after the timer gets to 2 more than dutyc_msb
;The calculation done if duty cycle is below half
btfsc dutyc_msb, 7
goto chk_1 ;wait for timer
run
;This is where your calculation code goes
calc_fin
clrwdt ;reset watchdog
; Check output state. we mustn't come back
; here before checking the output state.
btfsc out_dc_port, out_dc
goto chk_0
goto chk_1
Last edited by Diver300; 12th November 2008 at 11:19 AM. Reason: formating improvements | |
| |
| | #6 |
|
The low frequency of instruction execution (1 MHz) places rather strict limits on the frequency and resolution of the software generated PWM. I was a mentor for a LEGO robotics team for several years. IIRC there were only five power levels for the motors and we got along rather well. If you can tolerate 8 levels (0.0%, 12.5%, 25%, etc) and a frequency of about 122 Hz, I believe that it is doable. Use a prescaler of 4 so that the timer overflows every 1024 instruction cycles. With an 8 level resolution a PWM cycle is 8196 usec for a frequency of about 122 Hz. Each time the timer overflow execute the PWM routine to see if an output needs to be changed. Then execute the rest of your code and afterwards go into a wait loop checking for the timer to overflow, i.e. for the msb to change from 1 to 0. Every 8 PWM execcutions you reset the PWM and start a new cycle. You have a total of 1024 instruction cycles to get all your computing done. If you can get your computing done in 512 cycle then you can double the frequency or the resolution. If you can get everything done in 256 cycle, you could double again. I have some code somewhere that works this way. I don't know how long it will take me to find it or if I can find it at all. Last edited by skyhawk; 12th November 2008 at 03:35 PM. | |
| |
| | #7 |
|
Here is a code fragment taken from a working program. It should work, but there was a bit of editting so no guarantees. Go to PWM each time the timer overflows. You load the duty cycle that you desire into Sduty_0 and Sduty_1 anytime you need to change. Code: PWM movf Duty_0,w subwf PWM_cntr,w btfsc STATUS,Z bsf SPORT,D0_pin ; turn on output 0 ; movf Duty_1,w subwf PWM_cntr,w btfsc STATUS,Z bsf SPORT,D1_pin ; turn on output 1 ; movf SPORT,w movwf GPIO decfsz PWM_cntr,f goto Work ; ; Setup for next cycle ; movlw PWM_cycle movwf PWM_cntr ; reset PWM_cntr bcf SPORT,D0_pin ; Clear output 0 bcf SPORT,D1_pin ; Clear output 1 ; movf Sduty_0,w movwf Duty_0 movf Sduty_1,w movwf Duty_1 ; Work your code here SPORT ; shadow variable for GPIO Sduty_0 ; shadow variable for Duty_0 Sduty_1 ; shadow variable for Duty_1 Duty_0 ; duty cycle variable for D0 Duty_1 ; duty cycle variable for D1 PWM_cntr ; counter for PWM timing And you need these defines: #define PWM_cycle 8 #define D0_pin 0 #define D1_pin 1 | |
| |
| | #8 |
|
Here's a 2 channel 4 bit (0-15) PWM routine that runs at 4.7kHz. Not bad for a 1MHz chip. Code: ;M0 & M1 are the port numbers for the motors ;PWM1 is motor 1 speed (0-15) ;PWM2 is motor 2 speed (0-15) DoPWM bsf Shadow,M0 ;turn motor 0 on bsf Shadow,M1 ;and motor 1 movlw 1 ;preload W PwmLoop subwf PWM1,F ;sub 1 from PWM1 btfss STATUS,DC ;was there a borrow from bit 4 bcf Shadow,M0 ;yes so turn motor 0 off subwf PWM2,F ;now do second channel btfss STATUS,DC bcf Shadow,M1 movfw Shadow ;copy shadow register movwf GPIO ;to I/O register movlw 1 ;reload W addwf Count,F ;inc count but set flags btfss STATUS,DC ;have we been around 16 times goto PwmLoop ;no, so go around inner loop btfss STATUS,Z ;have we done 256 times goto DoPWM ;no so repeat outer loop retlw 0 ;done ![]() Every time you call DoPWM it does 16 complete cycles. I would read your sensors and then call DoPWM 3 times to give a loop time of around 100Hz. For anyone interested, the above code is possible because it uses the Digit Carry (DC) bit. The DC status bit gets set when there is a carry from bit 3 to 4 or in the case of subtraction it is cleared when there is a borrow from bit 4. Have fun, Mike. | |
| |
| | #9 |
|
Now that it has been established that it is possible to do software PWM with this chip, it would be nice if the OP would let us know what sort of control law he proposes, i.e. how is he going to establish the duty cycles for the PWM from his sensor readings?
| |
| |
| | #10 | |
| Quote:
| ||
| |
| | #11 |
| | |
| |
|
| Tags |
| pwm, software |
| Thread Tools | |
| Display Modes | |
| |
Similar | ||||
| Title | Starter | Forum | Replies | Latest |
| PIC 10F200 programming -- how ? | tiny2 | Micro Controllers | 5 | 10th April 2007 07:07 PM |
| software | chandu13 | Micro Controllers | 0 | 13th January 2007 05:56 AM |
| I need this software ... | Electronic boy | General Electronics Chat | 2 | 12th July 2003 09:58 AM |
| PCB Software | krishtriram | Micro Controllers | 6 | 11th July 2003 09:41 PM |
| Cad software | Chippie | General Electronics Chat | 14 | 11th July 2003 03:30 PM |