I had the need to generate some very low frequency PWM recently. As the inbuilt PWM does not go that low, I looked at writing my own. I know there is the software PWM but as that can only generate PWM whilst it is 'active' (as, I should say mine does) and given I wanted the capability of ramping the PWM up and down to provide a soft start, and soft stop, I thought I'd see if I could make my own.
These routines are capable of generating PWM from 10Hz to 999Hz, with a minimum duty of 1% and a maximum of 99%.
The ramp routines can ramp up from any value (1% minimum) to any value (99% max) and if required the ramp down will do the same in reverse.
I've tried to comment the code as much as possible, they form a working demonstration for the 12F1840 and should be easily portable to any processor.
I don't for a second assume that they are better than the software PWM library, but they gave me the flexibility I required.
They are rather memory hungry, needing about 1700 words of program space. This could be reduced by a few words by omitting the adjustable (potentiometer and ADC) routines if not needed. If you don't need the flexibility of being able to invert the PWM generation, these parts could also be omitted giving further savings.
'12F1840 demo for PWM generation'The Frequency can be from 10 Hz to 750 Hz'The Duty can be from 1% to 99%'First, set the frequency:'Set_Frequency(Frequency_In_Hz)'Set_Frequency(500) 'Sets a frequency of 500Hz'Then, set the duty:'Set_Duty(Duty_Required_In_Percent)'Set_Duty(25) 'Sets an on duty of 25% and an off duty of 75%'GenerateFixed_PWM will generate set number of PWM cycles, at'the current frequency and duty'GenerateFixed_PWM(Number_Of_Cycles, Invert_PWM)'Invert_PWM determines whether the duty of the PWM should be'inverted or not.'When inverted, a duty of 25% on and 75% off becomes 75% on and 25% off.'GenerateFixed_PWM(100,0) generates 100 cycles of non-inverted PWM at'the current frequency and duty'RampUp_PWM ramps the PWM at the current frequency from the start duty'to the end duty.'The start duty must be less than end duty. If not, no PWM will be generated.'The start can not be less than 1%, the end can not be more than 99%.'If these are exceeded, they will default to 1%, or 99%.'RampUp_PWM(Start_DUTY, End_Duty, Invert_PWM)'RampUp_PWM(0,99,0) generates a PWM that ramps from 1% to 99% which is not inverted'RampDn_PWM ramps the PWM at the current frequency from the start duty'to the end duty.'The start duty must be more than end duty. If not, no PWM will be generated.'The start can not be more than 99%, the end can not be more than 1%.'If these are exceeded, they will default to 1%, or 99%.'RampDn_PWM(Start_DUTY, End_Duty, Invert_PWM)'RampDn_PWM(99,0,0) generates a PWM that ramps from 99% to 1% which is not inverted'Set_Variable_PWM allows the duty to be set by a potentiometer connected to an ADC input'in this example, it is ADC0 = Pin 7 on the 12F1840'===='NOTE'===='After Set_Variable_PWM has been used, all PWM will be generated at the most recent 'duty set by the potentiometer. To return to another duty, Set_Duty must be used.'GenerateVariable_PWM generates a PWM where the duty is set by the potentiometer'as in Set_Variable_PWM.'To generate a fully variable PWM, call GenerateVariable_PWM from within a loop'GenerateVariable_PWM(Number_Of_Cycles, Invert_PWM)'Invert_PWM determines whether the duty of the PWM should be'inverted or not.'Repeat 100' GenerateVariable_PWM(10,0)'End Repeat'Generates 100 patterns of 10 cycles of PWM. The PWM duty is updated after each'pattern of 10 cycles of PWM have been generated.'===='NOTE'===='After GenerateVariable_PWM has been used, all PWM will be generated at the most recent 'duty set by the potentiometer. To return to another duty, Set_Duty must be used.'The final two routines are not implemented, they are from a program for the 16F1825'The idea is (or was) to provide a number of steps of positive and negative adjustment'depending on the position of the potentiometer. When the potentiometer is set to the'middle position, no adjustment is made. As the potentiometer is rotated left and right,'further steps of adjustment are made.'Set_PWMValues''and''Get_AdjustedPWM'Set_PWMValues'This increases the value of some fixed PWM duties by up to +/- 22%'The fixed values can then be used to call one of the other PWM generation routines''Example 1:'RampUp_PWM(0,SpMed,0)''would ramp up to the fixed PWM duty set by SpMed''Example 2:'Set_PWM_Values'RampUp_PWM(0,SpMed,0)''Would first adjust the value of SpMed depending on the position of a potentiometer'and then ramp the PWM up from zero to that adjusted value.'''To use these routines, these variables used in them would require dimensioning:''Dim SpHigh As Byte'Dim SpHmed As Byte'Dim SpMed As Byte'Dim SpLmed As Byte'Dim SpLow As Byte'Dim SpDead As Byte''Let SpHigh = 90'Let SpHmed = 75'Let SpMed = 50'Let SpLmed = 30'Let SpLow = 25'Let SpDead = 10'(c) Mark Stevenson 2020' 'Optimise A-d.h' 'Standard family chips#defineUSE_AD0True#defineUSE_AD1False#defineUSE_AD2False#defineUSE_AD2False#defineUSE_AD3False#defineUSE_AD4False#defineUSE_AD5False#defineUSE_AD6False#defineUSE_AD7False#defineUSE_AD8False#defineUSE_AD9False#defineUSE_AD10False#defineUSE_AD11False#defineUSE_AD12False#defineUSE_AD13False#defineUSE_AD14False#defineUSE_AD15False#defineUSE_AD16False#defineUSE_AD17False#defineUSE_AD18False#defineUSE_AD19False#defineUSE_AD20False#defineUSE_AD21False#defineUSE_AD22False#defineUSE_AD23False#defineUSE_AD24False#defineUSE_AD25False#defineUSE_AD26False#defineUSE_AD27False#defineUSE_AD28False#defineUSE_AD29False#defineUSE_AD30False#defineUSE_AD31False#defineUSE_AD32False#defineUSE_AD33False#defineUSE_AD34False#OptionExplicit#Chip12F1840,32#ConfigCP=On'Read protected'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@' +5V 1#DefinePin2PortA.5'2#DefinePin3PortA.4'3#DefinePin4PortA.3'4 - In only#DefinePWM_OutPortA.2'5#DefinePin6PortA.1'6#DefineADC_InPortA.0'7' Gnd 8'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@DirPin2OutDirPin3InDirPin4InDirPWM_OutOutDirPin6OutDirADC_InIn'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'Initialise PWMDimFreq_TimeAsLong'Holds the time of a complete cycle in uSDimPWM_OnTimeAsLong'Holds the 'On' part of the PWM timeDimPWM_OffTimeAsLong'Holds the 'Off' part of the PWM timeDimADC_ValueAsWord'Holds the value of the potDimLast_ADCAsWord'Check to see if pot position changedDimCurrent_DutyAsWord'Holds the most recent dutyLetFreq_Time=0LetPWM_OnTime=0LetPWM_OffTime=0LetADC_Value=0LetLast_ADC=0LetCurrent_Duty=0'Initialise Frequency and DutySet_Frequency(125)Set_Duty(10)'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Do'With a frequency of 125Hz, each complete cycle should take 8 mS to be generated'1000 cycles should equate to eight seconds of PWM (1000 x 0.008 = 8 Seconds)'The timings below are approximate, but should give an idea of their usageGenerateFixed_PWM(1000,0)'This generates around 10 seconds of fixed value PWM at 125Hz'PWM InvertedGenerateFixed_PWM(1000,1)'This generates around 10 seconds of fixed value PWM at 125Hz'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesRampUp_PWM(0,98,0)'This takes approximately 5 seconds to ramp from 0 - 98% at 125HzRampDn_PWM(98,0,0)'This takes approximately 5 seconds to ramp from 98 - 0% at 125Hz'PWM InvertedRampUp_PWM(0,98,1)'This takes approximately 5 seconds to ramp from 0 - 98% at 125HzRampDn_PWM(98,0,1)'This takes approximately 5 seconds to ramp from 98 - 0% at 125HzRepeat100'100 repeats of 10 cycles gives around 10 seconds of PWM at 125HzGenerateVariable_PWM(10,0)EndRepeat'Set duty after variable PWM has been usedSet_Duty(10)LoopSubSet_Frequency(InFrequencyAsLong)IfFrequency>750ThenLetFrequency=500EndIfIfFrequency<10ThenLetFrequency=10EndIfLetFreq_Time=7250/Frequency'This should give us the uS delay required to generate the PWM timeIfPWM_OnTime=0ThenSet_Duty(50)EndIfEndSubSubSet_Duty(InDutyRatioAsByte)IfFreq_Time=0ThenSet_Frequency(125)EndIfIfDutyRatio>99ThenLetDutyRatio=99EndIfIfDutyRatio<1ThenLetDutyRatio=1EndIfLetCurrent_Duty=DutyRatioLetPWM_OnTime=DutyRatioLetPWM_OffTime=100-PWM_OnTimeLetPWM_OnTime=Freq_Time*PWM_OnTimeLetPWM_OffTime=Freq_Time*PWM_OffTimeEndSubSubGenerateFixed_PWM(InCyclesAsWord,InInvert_PWMAsByte)IfInvert_PWM=0ThenRepeatCycles'Generate a few cycles of PWMLetPWM_Out=1RepeatPWM_OnTimeWait1uSEndRepeatLetPWM_Out=0RepeatPWM_OffTimeWait1uSEndRepeatEndRepeatElseRepeatCycles'Generate a few cycles of Inverted PWMLetPWM_Out=0RepeatPWM_OnTimeWait1uSEndRepeatLetPWM_Out=1RepeatPWM_OffTimeWait1uSEndRepeatEndRepeatEndIfEndSubSubGenerateVariable_PWM(InCyclesAsWord,InInvert_PWMAsByte)Set_Variable_PWMGenerateFixed_PWM(Cycles,Invert_PWM)EndSubSubRampUp_PWM(InStartDutyAsByte,InEndDutyAsByte,InInvert_PWMAsByte)'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesDimSt_PWMOnAsLongDimSt_PWMOffAsLongDimRampLoopAsByteIfStartDuty>99ThenLetStartDuty=99EndIfIfStartDuty<1ThenLetStartDuty=1EndIfIfEndDuty>99ThenLetEndDuty=99EndIfIfEndDuty<1ThenLetEndDuty=1EndIfIfFreq_Time=0ThenSet_Frequency(125)EndIfIfStartDuty=>EndDutyThenExitSubEndIfLetRampLoop=EndDuty-StartDutyLetSt_PWMOn=StartDutyLetSt_PWMOff=100-St_PWMOnLetSt_PWMOn=Freq_Time*St_PWMOnLetSt_PWMOff=Freq_Time*St_PWMOffIfInvert_PWM=0ThenRepeatRampLoop'Std PWM'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesRepeat5' 5 cycles of each "step"LetPWM_Out=1RepeatSt_PWMOnWait1uSEndRepeatLetPWM_Out=0RepeatSt_PWMOffWait1uSEndRepeatEndRepeatLetSt_PWMOn=St_PWMOn+Freq_TimeLetSt_PWMOff=St_PWMOff-Freq_TimeEndRepeatElseRepeatRampLoop'Inverted PWM'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesRepeat5' 5 cycles of each "step"LetPWM_Out=0RepeatSt_PWMOnWait1uSEndRepeatLetPWM_Out=1RepeatSt_PWMOffWait1uSEndRepeatEndRepeatLetSt_PWMOn=St_PWMOn+Freq_TimeLetSt_PWMOff=St_PWMOff-Freq_TimeEndRepeatEndIfLetCurrent_Duty=EndDutyEndSubSubRampDn_PWM(InStartDutyAsByte,InEndDutyAsByte,InInvert_PWMAsByte)'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesDimSt_PWMOnAsLongDimSt_PWMOffAsLongDimRampLoopAsByteIfStartDuty>99ThenLetStartDuty=99EndIfIfStartDuty<1ThenLetStartDuty=1EndIfIfEndDuty>99ThenLetEndDuty=99EndIfIfEndDuty<1ThenLetEndDuty=1EndIfIfFreq_Time=0ThenSet_Frequency(125)EndIfIfStartDuty<=EndDutyThenExitSubEndIfLetRampLoop=StartDuty-EndDutyLetSt_PWMOn=StartDutyLetSt_PWMOff=100-St_PWMOnLetSt_PWMOn=Freq_Time*St_PWMOnLetSt_PWMOff=Freq_Time*St_PWMOffIfInvert_PWM=0ThenRepeatRampLoop'Std PWM'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesRepeat5' 5 cycles of each "step"LetPWM_Out=1RepeatSt_PWMOnWait1uSEndRepeatLetPWM_Out=0RepeatSt_PWMOffWait1uSEndRepeatEndRepeatLetSt_PWMOn=St_PWMOn-Freq_TimeLetSt_PWMOff=St_PWMOff+Freq_TimeEndRepeatElseRepeatRampLoop'Inverted PWM'The ramp time can be reduced by lowing the repeat count in ramp up and ramp down routinesRepeat5' 5 cycles of each "step"LetPWM_Out=0RepeatSt_PWMOnWait1uSEndRepeatLetPWM_Out=1RepeatSt_PWMOffWait1uSEndRepeatEndRepeatLetSt_PWMOn=St_PWMOn-Freq_TimeLetSt_PWMOff=St_PWMOff+Freq_TimeEndRepeatEndIfLetCurrent_Duty=EndDutyEndSubSubSet_Variable_PWMLetADC_Value=ReadAD10(AN0,True)'Read the ADC. AN0 on the 12F1840 is pin 7IfADC_Value>990ThenLetADC_Value=990EndIfIfADC_Value<1ThenLetADC_Value=1EndIfLetADC_Value=ADC_Value/10'Give a value of 1 - 99IfADC_Value<>Last_ADCThenLetLast_ADC=ADC_ValueSet_Duty(ADC_Value)EndIfEndSub'These last two routines are not implemented, they are from a program for the 16F1825'The idea is (or was) to provide a number of steps of positive and negative adjustment'depending on the position of the potentiometer. When the potentiometer is set to the'middle position, no adjustment is made. As the potentiometer is rotated left and right,'further steps of adjustment are made.'This increases the value of some fixed PWM duties by up to +/- 22%'The fixed values are then used to call one of the other PWM generation routines''Example 1:'RampUp_PWM(0,SpMed,0)''would ramp up to the fixed PWM duty set by SpMed''Example 2:'Set_PWM_Values'RampUp_PWM(0,SpMed,0)''Would first adjust the value of SpMed depending on the position of a potentiometer'and then ramp the PWM up from zero to that adjusted value.'''To use these routines, the variables used in them would require dimensioning:''Dim SpHigh As Byte'Dim SpHmed As Byte'Dim SpMed As Byte'Dim SpLmed As Byte'Dim SpLow As Byte'Dim SpDead As Byte''Let SpHigh = 90'Let SpHmed = 75'Let SpMed = 50'Let SpLmed = 30'Let SpLow = 25'Let SpDead = 10SubSet_PWMValuesGet_AdjustedPWMIfAdjustment>0Then'The maximum number of steps are 22'Be aware that where this can result'in values going over 99 or under 1'this should be checked for.IfAdjustment>100Then'A positive adjustment requiredLetAdjustment=Adjustment-100LetSpHigh=90+AdjustmentLetSpHmed=75+AdjustmentLetSpMed=50+AdjustmentLetSpLmed=30+AdjustmentLetSpLow=25+AdjustmentLetSpDead=10+AdjustmentIfSpHigh>99ThenLetSpHigh=99EndIfElse'A negative adjustment requiredLetSpHigh=90-AdjustmentLetSpHmed=75-AdjustmentLetSpMed=50-AdjustmentLetSpLmed=30-AdjustmentLetSpLow=25-AdjustmentLetSpDead=10-AdjustmentIfSpDead>99ThenLetSpDead=1EndIfEndIfElseLetSpHigh=90LetSpHmed=75LetSpMed=50LetSpLmed=30LetSpLow=25LetSpDead=10EndIfEndSub'A method of using these adjusted steps is shown in Set_PWMValuesSubGet_AdjustedPWMLetADC_Value=ReadAD10(AN0,True)'Read the ADC. AN0 on the 12F1840 is pin 7IfADC_Value>990ThenLetADC_Value=990EndIfIfADC_Value<1ThenLetADC_Value=1EndIfLetADC_Value=ADC_Value/10'Give a value of 1 - 99LetAdjustment=0'Note: In the original prototype'The potentiometer is wired in "Reverse" so positive values'indicate that a negative adjustment has been requested.IfADC_Value>55ThenLetAdjustment=ADC_Value-55LetAdjustment=Adjustment/2'This should give a total of 22 steps in the negative directionEndIfIfADC_Value<45ThenLetAdjustment=45-ADC_ValueLetAdjustment=Adjustment/2'This should give a total of 22 steps in the positive directionLetAdjustment=Adjustment+100'Indicate it is a positive valueEndIfEndSub
Last edit: mkstevo 2020-05-07
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Sorry, I guess I didn't make that section as clear as it ought to be.
The bottom two routines are not used in the demonstration program, they are there to provide inspiration on how this could be achieved. There is a comment that says that they were taken from a program for the 16F1825 and that for the 12F1840 "ReadAD10(AN7)" would need changing to "ReadAD10(AN0)". I should perhaps update that so that for the 12F1840 they work correctly, it might be more obvious.
Code updated to remove references to AN7.
Code updated to include Optimisation for ADC.
Last edit: mkstevo 2020-05-07
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Your code attracted my interest as I needed a fixed every 20ms pwm with 1 to 19ms pulse width.
I used an every 1ms interrupt but with a faster interrupt could work to 1000Hz...and variable duty cycle.
I'll get back on this when (if) I sort it.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Your code says 0 to 1000 Hz pwm.
That would be every 1ms but then the 0 to 100% duty cycle would be 10us.
I don't think much could be done in 10us interrupt but gonna scope it...if my usb can cope.
like cheap dc motors in reclaimed toys to make a robot may not like high pwm frequencies...
not so low that it humms though.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Your code says 0 to 1000 Hz pwm.
That would be every 1ms but then the 0 to 100% duty cycle would be 10us.
I don't think much could be done in 10us interrupt but gonna scope it...if my usb can cope.
like cheap dc motors in reclaimed toys to make a robot may not like high pwm frequencies...
not so low that it humms though.
so..every 1ms if pwm=1% then on 10us off 990us
if pwm=99% then on 990us off 10us.
that's just for 1KHz...doh
Last edit: stan cartwright 2020-08-09
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I'm making progress Mike. You got to get into interrups mate. I am just learning like timer0 match and the 328 has 3 timers to play with. The timercalculator is worth installing.
I will start a new thread and say inspired by your idea of slower pwm.
my version will allow a program to run in the foreground I guess but it wil be interrupted every 10us so will still work?? I am still a gcb beginner
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Although i was pleased I got a 1KHz pwm to work in a short time and felt smug,
getting it to change frequency to 1Hz accuracy would it seems need a faster than 10uS interrupt...more like 1uS.
999Hz=1001.001001001 uS
998Hz=1002.004008016 uS
997Hz=1003.0090270812 uS
It looks like just add 1uS but doing my head in :)
adding 1uS changes frequency but not pwm % and I seem to be looking at this wrong ...or may be not possible...but still trying.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
For the use I needed this for, interrupts might not have worked too well. I'm not good with interrupts either and trying to do what I needed looked like very hard work using them.
I particularly required that the PWM could be "ramped" up and down, moving the motors (which vary from 12V types to 48V types) progressively when starting and stopping. I also needed to ramp up and down a solenoid, which again can vary in voltage.
A "linear" approach where:
some PWM is ramped up,
some switches tested,
more PWM generated,
switches tested,
if switches reach certain positions,
ramp PWM down,
reverse motors,
ramp PWM up,
test switches,
generate more PWM,
test switches,
if switches reach certain positions,
ramp PWM down,
change motors,
Ramp PWM up...
Is more suited to my application, and to my mind, easier for me to program and understand.
The motors are more suted to low frequency PWM, as are the solenoids. I only use PWM up to 250Hz, in order to give me some leeway in case I needed higher frequencies later, I limited my calculations to 1kHz.
Above a few 100Hz, the PWM modules in the (PIC) device peripherals would almost certainly be more suited. By running the device at a slow oscillator frequency, it is possible to generate lower frequencies than might be considered normally. Again, had it not been for the requirement I set myself of ramping up and down the PWM, this might have been the approach I would have taken. My original PicAxe program and design that these routines were intended to replace, did not have the ramp facilities, but I wanted to improve on that to make it worth my while in redesigning the circuit.
When it is working, it really is a joy to watch! We manufacture, sell and repair machines that are sold into amusement arcades. This test unit forms part of an automated test unit for the "mechanics" for the "Grabbing" machines that you would see in a seaside arcade where you might try to win a soft toy. This tests not only almost all of the mechanics we sell or have sold previously, it also tests most (very nearly all) of the competing products that we service. I simply connect up using an appropriate adapter cable and switch on - having selected a power supply of an appropriate voltage. The unit detects the type of mechanics fitted by testing the the operation of the switches in a particular sequence. By building a table of how the switches move under certain movements it then determines how to move the various types of mechanics in the "correct" sequence. After that, an automatic burn in test can be performed, testing the entire system repeatedly for as long as required to either prove the unit working following a repair, or to try to induce any intermittent faults that may be present. It does all of this with no intervention, allowing me to get on with something else. This means I can be doing two things at once, doubling my work rate!
Last edit: mkstevo 2020-08-13
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi Sir. I worked in the arcade games industry in the late 70's but trade died off in the 80's due to home "computers" so the company switched to making gambling machines.
These poker game machines were sent to southern Ireland and because of gambling laws,
did not pay out winnings in cash. It displayed on the screen "For Amusement Only".
Apparently if you won you told the bar staff who had a key to reset the machine and they paid you in cash...that's what the factory owner told me.
As for interrupts.. like many gcb things, they can seem deep and mysterious..and still are to me
but I think worth trying to grasp.
As for solenoids, I bought a 12V solenoid water/air valve but trouble driving it with pwm.
Maybe it is just to slow to respond.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I had the need to generate some very low frequency PWM recently. As the inbuilt PWM does not go that low, I looked at writing my own. I know there is the software PWM but as that can only generate PWM whilst it is 'active' (as, I should say mine does) and given I wanted the capability of ramping the PWM up and down to provide a soft start, and soft stop, I thought I'd see if I could make my own.
These routines are capable of generating PWM from 10Hz to 999Hz, with a minimum duty of 1% and a maximum of 99%.
The ramp routines can ramp up from any value (1% minimum) to any value (99% max) and if required the ramp down will do the same in reverse.
I've tried to comment the code as much as possible, they form a working demonstration for the 12F1840 and should be easily portable to any processor.
I don't for a second assume that they are better than the software PWM library, but they gave me the flexibility I required.
They are rather memory hungry, needing about 1700 words of program space. This could be reduced by a few words by omitting the adjustable (potentiometer and ADC) routines if not needed. If you don't need the flexibility of being able to invert the PWM generation, these parts could also be omitted giving further savings.
Last edit: mkstevo 2020-05-07
Very, very nice.
May I make a demo from this? Essentially, means I put in GitHub (demos in the distro).
Code comments.
Very cool.
Sorry, I guess I didn't make that section as clear as it ought to be.
The bottom two routines are not used in the demonstration program, they are there to provide inspiration on how this could be achieved. There is a comment that says that they were taken from a program for the 16F1825 and that for the 12F1840 "ReadAD10(AN7)" would need changing to "ReadAD10(AN0)". I should perhaps update that so that for the 12F1840 they work correctly, it might be more obvious.
Code updated to remove references to AN7.
Code updated to include Optimisation for ADC.
Last edit: mkstevo 2020-05-07
Your code attracted my interest as I needed a fixed every 20ms pwm with 1 to 19ms pulse width.
I used an every 1ms interrupt but with a faster interrupt could work to 1000Hz...and variable duty cycle.
I'll get back on this when (if) I sort it.
Your code says 0 to 1000 Hz pwm.
That would be every 1ms but then the 0 to 100% duty cycle would be 10us.
I don't think much could be done in 10us interrupt but gonna scope it...if my usb can cope.
like cheap dc motors in reclaimed toys to make a robot may not like high pwm frequencies...
not so low that it humms though.
Your code says 0 to 1000 Hz pwm.
That would be every 1ms but then the 0 to 100% duty cycle would be 10us.
I don't think much could be done in 10us interrupt but gonna scope it...if my usb can cope.
like cheap dc motors in reclaimed toys to make a robot may not like high pwm frequencies...
not so low that it humms though.
so..every 1ms if pwm=1% then on 10us off 990us
if pwm=99% then on 990us off 10us.
that's just for 1KHz...doh
Last edit: stan cartwright 2020-08-09
I just sorted this demo based on the 50Hz pwm code I had.
https://www.youtube.com/watch?v=kJ03zHD_g5I&feature=youtu.be
How to vary the pwm frequency ??
Last edit: stan cartwright 2020-08-09
I'm making progress Mike. You got to get into interrups mate. I am just learning like timer0 match and the 328 has 3 timers to play with. The timercalculator is worth installing.
I will start a new thread and say inspired by your idea of slower pwm.
my version will allow a program to run in the foreground I guess but it wil be interrupted every 10us so will still work?? I am still a gcb beginner
Although i was pleased I got a 1KHz pwm to work in a short time and felt smug,
getting it to change frequency to 1Hz accuracy would it seems need a faster than 10uS interrupt...more like 1uS.
999Hz=1001.001001001 uS
998Hz=1002.004008016 uS
997Hz=1003.0090270812 uS
It looks like just add 1uS but doing my head in :)
adding 1uS changes frequency but not pwm % and I seem to be looking at this wrong ...or may be not possible...but still trying.
Added a pot
do
pwm_percent=scale(readad (AN0),0,255,1,99)
loop
but can't figure frequency change
https://www.youtube.com/watch?v=sXdhdx3Qgas
For the use I needed this for, interrupts might not have worked too well. I'm not good with interrupts either and trying to do what I needed looked like very hard work using them.
I particularly required that the PWM could be "ramped" up and down, moving the motors (which vary from 12V types to 48V types) progressively when starting and stopping. I also needed to ramp up and down a solenoid, which again can vary in voltage.
A "linear" approach where:
some PWM is ramped up,
some switches tested,
more PWM generated,
switches tested,
if switches reach certain positions,
ramp PWM down,
reverse motors,
ramp PWM up,
test switches,
generate more PWM,
test switches,
if switches reach certain positions,
ramp PWM down,
change motors,
Ramp PWM up...
Is more suited to my application, and to my mind, easier for me to program and understand.
The motors are more suted to low frequency PWM, as are the solenoids. I only use PWM up to 250Hz, in order to give me some leeway in case I needed higher frequencies later, I limited my calculations to 1kHz.
Above a few 100Hz, the PWM modules in the (PIC) device peripherals would almost certainly be more suited. By running the device at a slow oscillator frequency, it is possible to generate lower frequencies than might be considered normally. Again, had it not been for the requirement I set myself of ramping up and down the PWM, this might have been the approach I would have taken. My original PicAxe program and design that these routines were intended to replace, did not have the ramp facilities, but I wanted to improve on that to make it worth my while in redesigning the circuit.
When it is working, it really is a joy to watch! We manufacture, sell and repair machines that are sold into amusement arcades. This test unit forms part of an automated test unit for the "mechanics" for the "Grabbing" machines that you would see in a seaside arcade where you might try to win a soft toy. This tests not only almost all of the mechanics we sell or have sold previously, it also tests most (very nearly all) of the competing products that we service. I simply connect up using an appropriate adapter cable and switch on - having selected a power supply of an appropriate voltage. The unit detects the type of mechanics fitted by testing the the operation of the switches in a particular sequence. By building a table of how the switches move under certain movements it then determines how to move the various types of mechanics in the "correct" sequence. After that, an automatic burn in test can be performed, testing the entire system repeatedly for as long as required to either prove the unit working following a repair, or to try to induce any intermittent faults that may be present. It does all of this with no intervention, allowing me to get on with something else. This means I can be doing two things at once, doubling my work rate!
Last edit: mkstevo 2020-08-13
Hi Sir. I worked in the arcade games industry in the late 70's but trade died off in the 80's due to home "computers" so the company switched to making gambling machines.
These poker game machines were sent to southern Ireland and because of gambling laws,
did not pay out winnings in cash. It displayed on the screen "For Amusement Only".
Apparently if you won you told the bar staff who had a key to reset the machine and they paid you in cash...that's what the factory owner told me.
As for interrupts.. like many gcb things, they can seem deep and mysterious..and still are to me
but I think worth trying to grasp.
As for solenoids, I bought a 12V solenoid water/air valve but trouble driving it with pwm.
Maybe it is just to slow to respond.