Program Listing '********************************************************************** ' Variables '********************************************************************** 'Work variables used by the button commands. but1work var byte but2work var byte but3work var byte 'Variable used to hold raw analog value. adval var word 'metermode determines which display mode the meter is in. ' Currently modes 1 - 4 are supported metermode var byte 'The actual mode: Freq, Duty, RPM metermode=1 'The initial mode 'Used in the frequency counter mode to determine samples per second. freqdur var word freqmult var word freqmode var byte freqcounter var long 'This one holds the counts 'Used in the duty cycle mode. pulsehigh var word 'Duration of high pulse used in Duty mode pulselow var word 'Duration of low pulse used in Duty mode duty var byte 'Calculated duty cycle 'Used in the RPM mode to determine samples per second. rpmdur var word rpmmult var word rpmmode var byte rpmcounter var long 'This one holds the counts 'Used to hold the data used by the hardware PWM command. pwmperiod var word 'This is the period for use with the HPWD command. pwmduty var word 'The duty period. 'This is a flag used to determine when to write the new PWM period ' data to the eeprom changepwm var byte changepwm = 0 '********************************************************************** ' EEPROM Data Storage '********************************************************************** 'The data command is used to store data in the eeprom. This data ' can be changed with the write command. 'PWM Out Period: low,high bytes data @0,192,38 'Freq data: Mode, dur low, dur high, mult low, mult high data @2,3,250,0,4,0 'RPM Data: Mode, dur low, dur high, mult low, mult high data @7,3,250,0,240,0 'The Read command reads eeprom data into actual variables. Note ' that the data statement stores the data in 8-bit chunks only. ' When we read or write the data, we must break down then data as ' well. 'As you see here I am reading in the lowbyte from eeprom address 0 ' and storing it in pwmperiod. In this case, the value will be 192 ' Next I will read in the highbyte and store it in pwmperiod. ' It's value is 38. Now pwmperiod is loaded and has a value of 9920. read 0,pwmperiod.lowbyte read 1,pwmperiod.highbyte read 2,freqmode read 3,freqdur.lowbyte read 4,freqdur.highbyte read 5,freqmult.lowbyte read 6,freqmult.highbyte read 7,rpmmode read 8,rpmdur.lowbyte read 9,rpmdur.highbyte read 10,rpmmult.lowbyte read 11,rpmmult.highbyte 'Later we will use the write command to alter the defaults '********************************************************************** ' Hardware '********************************************************************** 'Initialize the LCD pause 500 lcdwrite 2\3,outd,[INITLCD1,INITLCD2,TWOLINE,CLEAR,HOME,SCR] lcdwrite 2\3,outd,[CLEAR,HOME] 'Just to make sure it has cleared lcdwrite 2\3,outd,["Utility Meter"] pause 1000 lcdwrite 2\3,outd,[CLEAR,HOME,SCRRAM,"Freq"] 'Write a custom character to the CGRAM of the LCD display at position 0 ' and 1 'Empty Box lcdwrite 2\3,outd,[CGRAM +0,$00,$1f,$11,$11,$11,$1f,$00,$00] 'Filled Box lcdwrite 2\3,outd,[CGRAM +8,$00,$1f,$1f,$1f,$1f,$1f,$00,$00] 'Set up the timer interrupt. This will be used as a background task ' for the volt meter and the logic probes. settmr1 tmr1int1 ' Set timer1 mode 'The oninterrupt command tells the atom where to go on a particular ' interrupt. In this case, we will call the routine othermetertasks. oninterrupt tmr1int,othermetertasks enable tmr1int 'Turn on the interrupt 'Load pwmduty with 1/2 the pwmperiod value. This will give us a 50% ' duty cycle. pwmduty = pwmperiod/2 'Setup the initial pwm signal on pin 9. ' (mode=0 for pin8 and 1 for pin9) hpwm 1,pwmperiod,pwmduty '********************************************************************** 'Main loop. Check the button status and display the results '********************************************************************** mainloop: 'Check the status of S1 S2 and S3 Button 6,1,255,255,but1work,1,modechange 'S1 no auto repeat Button 5,1,100,1,but2work,1,valueup 'S2 Button 4,1,100,1,but3work,1,valuedn 'S3 'Depending on the value of metermode lets go display some readings Branch metermode, [mainloop,dispfreq,dispduty,disprpm,disppwm] 'If everything falls through wait a few milliseconds and go back and ' do it all again. pause 5 goto mainloop '********************************************************************** ' Display routines '********************************************************************** '-------------------------- 'Frequency counter mode #1 '-------------------------- dispfreq: 'count for the duration set up by the freqdur variable. count 11,freqdur,freqcounter 'Set up our counter on pin 11. 'Display the returned count. Depending on the duration we need ' to multiply the value so that it is correct. This gives us our ' samples per second. lcdwrite 2\3,outd,[SCRRAM+64,dec freqcounter*freqmult,_ " Hz "] 'Here we are just going to display the samples per second mode so the ' user knows which mode they are in. if freqmode = 1 then lcdwrite 2\3,outd,[SCRRAM+75,"1/s " ] elseif freqmode = 2 lcdwrite 2\3,outd,[SCRRAM+75,"2/s " ] elseif freqmode = 3 lcdwrite 2\3,outd,[SCRRAM+75,"4/s " ] elseif freqmode = 4 lcdwrite 2\3,outd,[SCRRAM+75,"10/s " ] endif goto mainloop '-------------------------- 'Duty mode #2 '-------------------------- dispduty: 'Set up the initial values. The pulsin command will not change them ' if it times out. pulsehigh=0 pulselow=0 'First we will look for the duration of the highbit then the duration ' of the lowbit. pulsin 11,1,pulsehigh pulsin 11,0,pulselow 'Calculate the duty cycle based on the duration of each bit. duty = (pulsehigh *100) / (pulselow+pulsehigh) 'I don't want to display large numbers so if both of the returned values ' are greater than 10000 let's convert to milliseconds. if pulsehigh > 10000 and pulselow > 10000 then pulsehigh = pulsehigh /1000 pulselow = pulselow /1000 lcdwrite 2\3,outd,[SCRRAM+64,dec pulsehigh,"/",dec pulselow,_ " ms ",dec duty,"% "] else lcdwrite 2\3,outd,[SCRRAM+64,dec pulsehigh,"/",dec pulselow,_ " us ",dec duty,"% "] endif goto mainloop '-------------------------- 'RPM mode #3 '-------------------------- disprpm: 'count for the duration set up by the rpmdur variable. count 11,rpmdur,rpmcounter 'Set up our counter on pin 11 'Display the returned count. Depending on the duration we need ' to multiply the value so that its correct. This gives us our ' samples per second. lcdwrite 2\3,outd,[SCRRAM+64,dec rpmcounter*rpmmult," RPM "] 'Here we are just going to display the samples per second mode so the ' user knows which mode they are in. if rpmmode = 1 then lcdwrite 2\3,outd,[SCRRAM+75,"1/s " ] elseif rpmmode = 2 lcdwrite 2\3,outd,[SCRRAM+75,"2/s " ] elseif rpmmode = 3 lcdwrite 2\3,outd,[SCRRAM+75,"4/s " ] elseif rpmmode = 4 lcdwrite 2\3,outd,[SCRRAM+75,"10/s " ] endif goto mainloop '-------------------------- 'PWM OUT Setup Mode #4 '-------------------------- disppwm: 'Display the current pwmperiod and target frequency. We will use the ' formula greq=1000000/(period/20) lcdwrite 2\3,outd,[SCRRAM+64,dec pwmperiod," ",_ real (float 1000000 fdiv (float pwmperiod fdiv float 20))\1,_ "Hz " ] 'Check to see if we made any changes to the pwmperiod. If we did, then ' we are going to write the data to the eeprom. I could have written ' the data in the value change routines but this would cause too many ' writes. The eeprom can only accept a finite number of writes. ' Somewhere on the order of 10 million. By doing the write when all ' the adjustments are done we minimize the number of writes. if changepwm = 1 then changepwm = 0 write 0,pwmperiod.lowbyte write 1,pwmperiod.highbyte endif goto mainloop '********************************************************************** 'Button Processing Routines '********************************************************************** '----------------------------------- 'S1 the mode button '----------------------------------- modechange: metermode = metermode + 1 'Increment the mode ' We only have 4 modes so wrap it if its greater than 4 if metermode > 4 then metermode = 1 endif 'Display the current mode on the LCD if metermode = 1 then lcdwrite 2\3,outd,[SCRRAM,"Freq "] elseif metermode = 2 lcdwrite 2\3,outd,[SCRRAM,"Duty "] elseif metermode = 3 lcdwrite 2\3,outd,[SCRRAM,"RPM "] elseif metermode = 4 lcdwrite 2\3,outd,[SCRRAM,"OUT "] endif goto mainloop '----------------------------------- 'S2 the value up button '----------------------------------- valueup: '---------- 'PWM OUT '---------- 'We only want to make changes if we are in PWM Out mode if metermode = 4 then pwmperiod = pwmperiod + 1 'Make the change 'Set the write flag. We will write the data to eeprom when we are ' done changepwm = 1 'Wrap the value if it is greater than 16383. if pwmperiod > 16383 then pwmperiod = 0 endif 'Set the duty and update the hardware. The change will take affect ' immediately. pwmduty = pwmperiod/2 hpwm 1,pwmperiod,pwmduty 'Display the period and target frequency lcdwrite 2\3,outd,[SCRRAM+64,dec pwmperiod," ",_ real (float 1000000 fdiv (float pwmperiod fdiv float 20))\1,_ "Hz " ] goto mainloop endif '------------------ 'Frequency Counter '------------------ 'We only want to adjust the frequency counter sample rate if we are in ' mode 1 if metermode = 1 then freqmode = freqmode + 1 'Change the sample rate mode 'We only support 4 sample rates if freqmode > 4 then freqmode = 1 endif 'Set up the duration and multipliers for each sample rate if freqmode = 1 then freqdur = 1000 freqmult = 1 elseif freqmode = 2 freqdur = 500 freqmult = 2 elseif freqmode = 3 freqdur = 250 freqmult = 4 elseif freqmode = 4 freqdur = 100 freqmult = 10 endif 'Write the new data to the eeprom write 2,freqmode write 3,freqdur.lowbyte write 4,freqdur.highbyte write 5,freqmult.lowbyte write 6,freqmult.highbyte goto mainloop endif '------------------ 'RPM '------------------ 'We only want to adjust the RPM sample rate if we are in mode 3 if metermode = 3 then rpmmode = rpmmode + 1 'Make the change 'We only support 4 sample rates if rpmmode > 4 then rpmmode = 1 endif 'Set up the duration and multipliers for each sample rate if rpmmode = 1 then freqdur = 1000 freqmult = 60 elseif rpmmode = 2 freqdur = 500 freqmult = 120 elseif rpmmode = 3 freqdur = 250 freqmult = 240 elseif rpmmode = 4 rpmdur = 100 rpmmult = 600 endif 'Write the new data to the eeprom write 7,rpmmode write 8,rpmdur.lowbyte write 9,rpmdur.highbyte write 10,rpmmult.lowbyte write 11,rpmmult.highbyte goto mainloop endif goto mainloop '----------------------------------- 'S3 The Value Down Button '----------------------------------- 'Currently, we only use S3 to change the PWM period value valuedn: 'We only want to make changes if we are in PWM Out mode if metermode = 4 then pwmperiod = pwmperiod - 1 'Make the change 'Set the write flag. We will write the data to eeprom when we are ' done changepwm = 1 'Wrap the value if it is less than zero. Remember we don't have ' signed numbers so we test to see if the word wrapped if pwmperiod > 16384 then pwmperiod = 16383 endif 'Set the duty and update the hardware. The change will take effect ' immediately. pwmduty = pwmperiod/2 hpwm 1,pwmperiod,pwmduty 'Let's display the period and target frequency lcdwrite 2\3,outd,[SCRRAM+64,dec pwmperiod," ",_ real (float 1000000 fdiv (float pwmperiod fdiv float 20))\1,_ "Hz " ] goto mainloop endif goto mainloop Disable ;disable interrupts so it can not call its self. '********************************************************************** 'This is our background handler routine ' We scan the input lines for voltmeter and logic probes. '********************************************************************** othermetertasks: 'Pull in the analog data from the first analog pad and place into adval adin AX0,3,AD_RON,ADval 'The adin command returns a 10-bit value. This gives us 1024 steps of ' 0-5 volts. So for 5v thats .0048828 per unit. i just round it to ' .00489. lcdwrite 2\3,outd,[SCRRAM+11,_ real (float ADval fmul float 0.00489)\2,"v"] 'Let me explain the above command a little bit. ' 2\3,outd ' This sets up the pins that are tied to the LCD display. The Atom ' is so versatile that you can use just about any pin combination ' for the LCD. This also means you can use 2 LCD's at once. ' real(expression)\2 ' Display a floating point number. The \2 says only display the ' first two decimal places. ' float adval and float 0.00489 ' While the Atom does support full 32-bit math internally it only ' supports floating point via built in commands. What we are doing ' here is telling The Atom to convert these values to IEEE 754 ' (floating point) format internally. ' fmul ' This is a floating point function. It will take the converted ' values and do a floating point multiply. ' So put it all together and the Atom is multiplying adval by .00489 ' and display the results. 'Now test the logic probes. lcdwrite 2\3,outd,[SCRRAM+6,in0,in1," "] ' When I initialized the LCD earlier I built two characters in the ' LCD character RAM of the LCD at positions 0 and 1. Position 0 ' is an empty box and position 1 is a filled box. Now when I ' write ASCII value 0 the LCD will display an empty box. Likewise ' when I write ASCII value 1 the LCD will display a filled box. The ' in0 and in1 commands will return the pin state for pins 0 and 1. ' The state will be 0 or 1. Now when I write those states a empty ' or filled box will be displayed depending on the state of the pin. resume 'this is how we end a interrupt task