{"id":883,"date":"2016-01-20T12:36:56","date_gmt":"2016-01-20T17:36:56","guid":{"rendered":"https:\/\/billthompson.us\/bt\/?p=883"},"modified":"2019-03-06T09:54:09","modified_gmt":"2019-03-06T14:54:09","slug":"motor-control-pic-source-code","status":"publish","type":"post","link":"https:\/\/billthompson.us\/bt\/motor-control-pic-source-code\/","title":{"rendered":"Motor Control Source Code"},"content":{"rendered":"<pre>\r\n; motor servo program for 16f1823\r\n; Bill Thompson - 03-29-2015\r\n;\r\n; Revision 2 - Fall 2018\r\n\r\n; 09\/29\/2018 many changes to use internal comparator\r\n; 10\/23\/2018 first running version with internal comparator\r\n; 10\/28\/2018 code cleanup, no functional changes\r\n; 10\/30\/2018 enable brownout reset & watchdog timer\r\n; 11\/15\/2018 reorder interrupt logic for speed\r\n; 11\/21\/2018 added 3rd speed to encoder\r\n; 11\/24\/2018 added provision for 8mHz xtal, 32 mHz pll cpu clock\r\n; 12\/03\/2018 add debounce code for encoder\r\n; 12\/28\/2012 tweaks for ramp code due to changes in timing from debounce revs\r\n; 03\/03\/2019 all port write now to latch instead of direct\r\n; 03\/04\/2019 remove hysteresis on encoder port\r\n; 03\/04\/2019 add delayed eeprom write enable at startup\r\n\r\n;------------------------------------------------------\r\n;conditional options\r\n;#define \trampup\t\t;comment this out to disable ramp-up\r\n#define \tmhz8\t\t;comment this out to use 4mhHz xtal\r\n;------------------------------------------------------------\r\n\r\n; PORT bit definitions\r\n\r\n; port A\r\n;tach0\tRA0\t\t;+ input from tachometer\r\n;tach1\tRA1\t\t;- input from tachometer\r\n;gpout\tRA2\t\t;output to integrator\r\n\r\n; port C\r\n;gp78\tRC2\t\t;low for 78 speed\r\n;gp33\tRC3\t\t;low for 33 speed\r\n                        ;both high for 45 speed\r\n\r\n;encode\t    RC0-1\t;rotary encoder bits\r\n;speed sel  RC2-3\r\n;avail      RC4-5\t;available\r\n\r\n\r\n\tlist\t\tp=16F1823\r\n\t#include\t<p16f1823.inc>\r\n\r\n;-------------------------------------------------------------\r\n;\r\n; CONFIGURATION WORD SETUP\r\n; use 8mHz xtal with 4x pll for system clock of 32mHz\r\n; or 4mHz xtal with 4x pll for system clock of 16mHz\r\n; reset pin disabled, lv pgm off\r\n; enable brownout reset and watchdog timer 10\/25\/2018\r\n; add provision for 8mHz xtal, 32 mHz cpu clock 11\/24\/2018\r\n;\r\n;------------------------------------------------------------------------------    \r\n\r\n; configuration in Microchip convention\r\n#ifdef\tmhz8\r\ncfg1a\tequ\t_FOSC_HS & _WDTE_SWDTEN & _PWRTE_ON & _MCLRE_OFF & _CP_OFF\r\ncfg1\tequ\tcfg1a & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF\r\n#else\r\ncfg1a\tequ\t_FOSC_XT & _WDTE_SWDTEN & _PWRTE_ON & _MCLRE_OFF & _CP_OFF\r\ncfg1\tequ\tcfg1a  & _CPD_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF\r\n#endif\r\n\r\ncfg2\tequ\t_WRT_OFF & _PLLEN_ON & _STVREN_ON & _BORV_HI & _LVP_OFF\r\n\r\n; configuration in plain binary\r\n; use _FOSC_XT for 4mHz xtal or _FOSC_HS for 8mHz xtal\r\n#ifdef\tmhz8\r\n\t__CONFIG _CONFIG1, b'00111110001010' ;0 0 1 11 1 1 0 0 01 010 -- 00 1111 1000 1010 -- 0f8a\r\n#else\r\n\t__CONFIG _CONFIG1, b'00111110001001' ;0 0 1 11 1 1 0 0 01 001 -- 00 1111 1000 1001 -- 0f89\r\n#endif\r\n\t__CONFIG _CONFIG2, b'01101111111111' ;0 1 1 0 1 1 111 1 11 11 -- 01 1011 1111 1111 -- 1bff\r\n\r\n;------------------------------------------------------------------------------\r\n; VARIABLE DEFINITIONS\r\n;\r\n;initial timer values for speed determination\r\n;assuming 32mHz timer clock (8mHz xtal)\r\n;or 16mHz timer clock (4mHz xtal)\r\n;these are the values in eeprom the first time\r\n;the pic is powered up. they are then\r\n;updated by encoder adjustments as needed.\r\n\r\n;updated these from actual measurement - thorens td-124\t04\/20\/2015\r\n;11\/28\/2018 add data for 8mHz xtal\r\n\r\n#ifdef\tmhz8\r\nl78\tequ\t0x86\t\t;78 low byte\r\nh78\tequ\t0xea\t\t;78 high byte\r\nl45\tequ\t0x71\t\t;45 low byte\r\nh45\tequ\t0xda\t\t;45 high byte\r\nl33\tequ\t0x3c\t\t;33 low byte\r\nh33\tequ\t0xcd\t\t;33 high byte\r\n\r\n#else\r\n\r\nl78\tequ\t0x53\t\t;78 low byte\r\nh78\tequ\t0xf5\t\t;78 high byte\r\nl45\tequ\t0x4b\t\t;45 low byte\r\nh45\tequ\t0xed\t\t;45 high byte\r\nl33\tequ\t0xa9\t\t;33 low byte\r\nh33\tequ\t0xe6\t\t;33 high byte\r\n#endif\r\n\r\n;speed limits for encoder adjustments (high byte only)\r\n#ifdef\tmhz8\r\nhilim\tequ\td'240'\t\t;high speed limit\r\nlolim\tequ\td'100'\t\t;low speed limit\r\n\r\n#else\r\n\r\nhilim\tequ\td'251'\t\t;high speed limit\r\nlolim\tequ\td'200'\t\t;low speed limit\r\n#endif\r\n\r\n; encoder speed constants\r\n; change step size depending on\r\n; speed of encoder rotation\r\n\r\ne_slow\tequ\t1\t\t;1 step when slow\r\ne_med\tequ\t8\t\t;8 steps when med.\r\ne_fast\tequ\t32\t\t;32 steps when fast\r\n\r\n;encoder debounce count read encoder mult times\r\n;all reads must agree for valid change\r\n;# of encoder reads = dcount+1\r\ndcount\tequ\t3\t\t;repeat encoder read 4 times\r\n\r\n;ramp speed variables\r\n#ifdef\tmhz8\r\nrampspdh\tequ\t0x40\t;8 mHz ramp speed divider 0x01=fast - 0xff=slow\r\n#else\r\nrampspdh\tequ\t0x20\t;4 mHz ramp speed divider 0x01=fast - 0xff=slow\r\n#endif\r\n\r\n;rampspd can be increased for further slowing of ramp\r\n;it may get shifted left by up to 4 bits at\r\n;higher motor speed so 0x08 is max legal value\r\n\r\nrampspd\t\tequ\t1\t;fine ramp speed 0x08 max.\r\n\r\nstartspd\tequ\t0x7f\t;high byte motor start speed\r\n\r\n;------------------------------------------------------------------------------\r\n; Data Memory\r\n;------------------------------------------------------------------------------\r\n\r\n    CBLOCK 0x20 ; all data memory in bank 0\r\n\r\n;ram storage for speed parameters\r\n;loaded from eeprom during initialization\r\n;holds speed setting for 3 speeds which may\r\n;be updated by the encoder during operation and\r\n;then updated in eeprom.\r\nspl78\t\t\t\t;78\r\nsph78\r\n\r\nspl45\t\t\t\t;45\r\nsph45\r\n\r\nspl33\t\t\t\t;33\r\nsph33\r\n\r\n;16 bit timer 1 preset storage\r\n;holds countdown value used to determine motor speed.\r\n;loaded from above depending on speed selection\r\ntiml\r\ntimh\r\n\r\n;ramp timer follows timl:timh ramping up and jumping down\r\n;if rampup is enabled so abrupt increase in speed does not throw belts!\r\nrtiml\r\nrtimh\r\nrampcnt\t\t\t\t;holds ramp speed for countdown\r\nrampcnth\t\t\t;holds ramp speed divider for countdown\r\nsptemp\t\t\t\t;temp for ramp speed calc\r\n\r\n;32 mS 8 bit counter for encoder rotation speed\r\nenctim\r\n\r\n;20 second 16 bit counter for eeprom updates\r\neetiml\r\neetimh\r\n\r\n;storage for rotary encoder operations\r\n;store encoder bits\r\nencold\r\nencnew\r\n\r\n;debounce storage\r\nelast\t\t\t\t;debounce last read \r\nencnt\t\t\t\t;debounce counter\r\n\r\n;temp for encoder calculations.\r\netmp\r\netmp2\r\n\r\n;output encoder data\r\nencspd\t\t\t\t;encoder speed 0=no change 1=slow 8=fast\r\nencdir\t\t\t\t;encoder direction 0=ccw 1=cw\r\nenclast\t\t\t\t;holds last state for debounce\r\n\r\n;eeprom read-write address pointer\r\nee_addr\t\t\t\t;eeprom address read\/write\r\n\r\n;counter for initial integrator reset to make sure\r\n;motor starts up slowly\r\ncounta\r\ncountb\r\ncountc\r\n\r\n;delay enabling eeprom writes at startup\r\nstdel\r\n\r\n;end of data memory\r\n\tENDC\r\n\t\r\n;eeprom address pointers for motor speed data\r\n;these point to physical addr starting at 0xf010\r\n;and are used for eeprom read\/write operations\r\nee\tequ\t0x10\r\n\r\nel78\tequ\tee\t\t;78 rpm\r\neh78\tequ\tee+1\r\n\r\nel45\tequ\tee+2\t\t;45 rpm\r\neh45\tequ\tee+3\r\n\r\nel33\tequ\tee+4\t\t;33 rpm\r\neh33\tequ\tee+5\r\n\r\n;------------------------------------------------------------------------------\r\n;\r\n; EEPROM INITIAL PROGRAMMING\r\n; The 16F1823 has 256 bytes of non-volatile EEPROM, starting at address 0xF000\r\n; This is referenced as 0x00-0xFF in read\/write operations\r\n; \r\n;------------------------------------------------------------------------------\r\n\r\nUSRID\tORG\t0X8000\t\t\t;0x334578 USER id\r\n\tDE\t0x33\r\n\tDE\t0X45\r\n\tDE\t0X78\r\n\tDE\t0\r\n\r\nDATAEE\tORG\t0xf000\r\n\tDE\t\"BillThompson.us\"\t;Place creator at address 0-14\r\n\r\n\tORG\t0xf010\t\t\t;initial speed data in eeprom 0x10-0x15\r\n\tDE\tl78\t;78rpm\t\t;programmed at burn time, updated later\r\n\tDE\th78\t\t\t;by encoder\r\n\tDE\tl45\t;45rpm\r\n\tDE\th45\r\n\tDE\tl33\t;33rpm\r\n\tDE\th33\r\n\r\n;------------------------------------------------------------------------------\r\n; RESET VECTOR\r\n;------------------------------------------------------------------------------\r\n\r\n\tORG     0x0000\t\t\t;processor reset vector\r\n\tGOTO    init\t\t\t;initialize and start pgm\r\n\t\r\n;------------------------------------------------------------------------------\r\n; INTERRUPT SERVICE ROUTINE\r\n; there are 2 interrupts, one from the tachometer when it\r\n; changes state, the other from the timer when it overflows.\r\n; the hardware integrator adjusts the motor speed to get a 50%\r\n; duty cycle.\r\n; If the interrupt is not the timer it must be the tachometer.\r\n;------------------------------------------------------------------------------\r\n\r\n\tORG      0x0004\r\n\r\n;16f1823 has automatic context save for interrupts\r\n;see if interrupt was from the timer\r\n;if yes, output low (integrate up), stop timer, preset timer\r\n;if no, its the tachometer, output high (integrate down), restart timer (already preset)\r\n;write port directly to save banksel 05\/19\/2015\r\n;reverse interrupt order for speed 11\/15\/2018\r\n;write to latch, not port for integrator drive 03\/03\/2019\r\n;\r\n\tbanksel 0\t\t;could be in any bank when interrupt happens\r\n\tbtfss\tPIR1,TMR1IF\t;a timer interrupt?\r\n\tgoto\ttach1\t\t;no, its tachometer, go handle it\r\n;---------------------------------------------------------\r\n;interrupt is from timer 1 overflow\r\n;RA2 is the only port A output, clearing the whole port\r\n;only affects RA2.\r\ntimi1\r\n\tbanksel\tLATA\t\t;use latch 03\/03\/2019\r\n\tclrf\tLATA\t\t;out low, integrate up\r\n\tbanksel\t0\r\n\t\r\n\tbcf\tT1CON,TMR1ON\t;stop timer\r\n\r\n\tmovf\trtiml,W\t\t;preset timer to countdown value\r\n\tmovwf\tTMR1L\t\t;from speed ramp counter 05\/11\/2015\r\n\tmovf\trtimh,W\r\n\tmovwf\tTMR1H\r\n\r\n\tbcf\tPIR1,TMR1IF\t;clear timer interrupt\r\n\tretfie\t\t\t;return from interrupt\r\n;---------------------------------------------------------\r\n;interrupt is from tachometer comparator state change\r\ntach1\r\n\tbanksel\tLATA\t\t;use latch 03\/03\/2019\r\n\tbsf\tLATA,RA2\t;out high, integrate down\r\n\tbanksel\t0\r\n\t\r\n\tbsf\tT1CON,TMR1ON\t;restart timer (already preset)\r\n\tclrf\tPIR2\t\t;clear comparator interrupt\r\n\tretfie\t\t\t;return from interrupt\t\t\r\n;---------------------------------------------------------\r\n\t\r\n;--------------------------------------------------------------------\r\n; main program starts here\r\n; initialize everything\r\n;--------------------------------------------------------------------\r\n\r\ninit\r\n\tbanksel\tINTCON\r\n\tclrf\tINTCON\t\t;make sure interrupts are disabled\r\n\r\n\tbanksel\tWDTCON\t\t;set up watchdog\r\n\tmovlw\tb'00001110'\t;set watchdog 128ms time and disable for now\r\n\tmovwf\tWDTCON\r\n\r\n\tbanksel\tOSCCON\t\t;xtal clock, PLL\r\n\tmovlw\tb'10000000'\r\n\tmovwf\tOSCCON\r\n\r\n; set analog inputs for comparator\r\n\tbanksel\tANSELA\t\t;RA0-1 analog inputs for comparator\r\n\tmovlw\tb'00000011'\t;all other inputs digital\r\n\tmovwf\tANSELA\r\n\tclrf\tANSELC\t\t;RC port all digital - same bank as ansela\r\n\t\r\n; set up comparator\r\n\tbanksel\tCM1CON0\t\t\r\n\tmovlw\tb'10000110'\t;enable comparator w\/ normal power, hysteresis\r\n\tmovwf\tCM1CON0\r\n\tmovlw\tb'11000000'\t;interrupt on both pos and neg transition\r\n\tmovwf\tCM1CON1\t\t;cm1con1 is in same bank as cm1con0\r\n\r\n;set up timer 1 for system clock, no prescaler, no sync\r\n\tbanksel\tT1CON\r\n\tmovlw\tb'01000100'\t;tmr1 sys clock, no sync, timer stopped\r\n\tmovwf\tT1CON\r\n\tclrf\tT1GCON\t\t;no timer 1 gate control\r\n\t\r\n\tbanksel\tOPTION_REG\r\n\tmovlw\tb'00000111'\t;global weak pullup,\r\n\tmovwf\tOPTION_REG\t;tmr0 sys clock with 1\/256 prescale\r\n\t\r\n; weak pullups on RC2,3\r\n\tbanksel\tWPUC\t\t;weak pullups on RC2-3\r\n\tmovlw\tb'001100'\t;for speed sel switch\r\n\tmovwf\tWPUC\r\n\r\n\tclrf\tWPUA\t\t;no weak pullups on PORTA\r\n\r\n\tbanksel\tPIE1\r\n\tclrf\tPIE1\t\t;disable all PIE1 interrupts except\r\n\tbsf\tPIE1,TMR1IE\t;enable timer 1 overflow interrupt\r\n\tclrf\tPIE2\t\t;disable all PIE2 interrupts except\r\n\tbsf\tPIE2,C1IE\t;enable comparator 1 interrupt\r\n\r\n\tbanksel\tPIR1\r\n\tclrf\tPIR1\t\t;clear any pending timer interrupts\r\n\tclrf\tPIR2\t\t;and comparator interrupts\r\n\r\n; set port I\/O types and preset encoder to current state\r\n\tbanksel\tTRISA\r\n\tmovlw \tB'111011'\t;RA2 & RC<4-5> = outputs\r\n\tmovwf\tTRISA \t\t;RA<4-5> inputs for xtal\r\n\tmovlw\tB'001111'\t;RC0-1 encoder inputs, RC<2-3> speed sel.inputs\r\n\tmovwf\tTRISC\t\t;and RC4-5 enc.as outputs\r\n\r\n\tbanksel\tLATA\r\n\tclrf\tLATA\t\t;clear port a latches\r\n\tclrf\tLATC\t\t;and port c\r\n\tbanksel\t0\r\n\r\n;-------------------------------------------------------------\r\n\t\r\n;start things in a known state\t\r\n;timer 1\r\n\tbanksel\t0\t\t;back to bank 0\r\n\tclrf\ttiml\t\t;set rpm timer at start speed\r\n\tclrf\trtiml\t\t;and ramp timer\r\n\r\n\tmovlw\tstartspd\t;initial motor start speed (high byte only)\r\n\tmovwf\ttimh\t\t;main and ramp timers\r\n\tmovwf\trtimh\r\n\t\t\r\n\tmovlw\trampspd\t\t;set ramp speed\r\n\tmovwf\trampcnt\r\n\r\n;8 bit counter for encoder speed\r\n\tclrf\tenctim\t\t;set encoder speed counter to slow (0xff)\r\n\tdecf\tenctim,f\r\n\r\n;16 bit counter for eeprom update\r\n\tclrf\teetiml\t\t;clear (stop) eeprom update counter\r\n\tclrf\teetimh\t\r\n\t\t\r\n;delayed eeprom write enable\r\n\tmovlw\t0xff\t\t;preset\r\n\tmovwf\tstdel\r\n\t\t\r\n;load speed data from eeprom\r\n;(as updated by last adjustments)\r\n\tmovlw\tel78\r\n\tcall\teerd1\t\t;get 78 data from eeprom\r\n\tmovwf\tspl78\t\t;store it in ram\r\n\tmovlw\teh78\r\n\tcall\teerd1\t\t;get 78 data from eeprom\r\n\tmovwf\tsph78\t\t;store it in ram\r\n\t\r\n\tmovlw\tel45\r\n\tcall\teerd1\t\t;get 45 data from eeprom\r\n\tmovwf\tspl45\t\t;store it in ram\r\n\tmovlw\teh45\r\n\tcall\teerd1\t\t;get 45 data from eeprom\r\n\tmovwf\tsph45\t\t;store it in ram\r\n\t\r\n\tmovlw\tel33\r\n\tcall\teerd1\t\t;get 33 data from eeprom\r\n\tmovwf\tspl33\t\t;store it in ram\r\n\tmovlw\teh33\r\n\tcall\teerd1\t\t;get 33 data from eeprom\r\n\tmovwf\tsph33\t\t;store it in ram\r\n\r\n;------------------------------------------------------------------------\r\n;start with integrator low\r\n;integrate down and wait, then start polling loop.\r\n;let integrator ramp up from low at start\t\r\n\tbanksel\tLATA\r\n\tbsf\tLATA,RA2\t;integrate down, stop motor\r\n\tbanksel\t0\r\n\t\r\n\tclrf\tcounta\t\t;preset counter\r\n\tclrf\tcountb\t\t;for startup delay\r\n\tmovlw\td'251'\t\t;4x longer startup delay\r\n\tmovwf\tcountc\t\t;when rampup function used\r\n\r\n;------------------------------------------------------------------------\r\n\t\r\n;delay while integrate down for slow start if using slow start ramp\r\n\r\nca\r\n\tincfsz\tcounta,f\r\n\tgoto\tca\r\n\tincfsz\tcountb,f\r\n\tgoto\tca\r\n#ifdef\trampup\t\t\t;longer startup delay if using rampup\r\n\tincfsz\tcountc,f\r\n\tgoto\tca\r\n#endif\r\n\r\n\tbanksel\tLATA\r\n\tclrf\tLATA\t\t;integrate up, start motor\r\n\tbanksel\t0\r\n;-------------------------------------------------------------------------\r\n\r\n\tbanksel\tWDTCON\t\t;now start watchdog timer\r\n\tbsf\tWDTCON,SWDTEN\r\n\r\n\tbanksel\t0\r\n\tmovlw\tb'11000000'\t;enable global and peripheral interrupts\r\n\tmovwf\tINTCON\r\n\t\r\n;------------------------------------------------------------------------\t\r\n\r\n;initialization done, main program loop starts here\r\n\r\n;speed ramp here to start motor gradually.\r\n;executed if \"rampup\" is defined\r\n;this may help prevent throwing belts!\r\n\r\n;ramp up motor slowly initially or if speed changes\r\n;executes in main loop before polling routines\r\n;rampcnt and rampcnth define how many main loops are executed\r\n;before the ramp code executes. This sets the ramp speed.\r\n;there is additional code to slow down the ramp as motor\r\n;approaches higher speeds.\r\n\r\npoll_ramp\r\n;----------------------------------------------------------\r\n\tbanksel\t0\r\n\tclrwdt\t\t\t;reset watchdog on each loop\r\n\r\n;-----------------------------------------------------------\r\n;delay enabling eeprom writes for a bit at startup\t\r\nsdel\r\n\tmovf\tstdel,W\t\t;read startup delay\r\n\tiorlw\t0\t\t;zero?\r\n\tbtfsc\tSTATUS,Z\r\n\tgoto\tsdel2\t\t;yes, do nothing\r\n\t\r\nsdel1\tmovf\tPORTC,W\t \t;yes, read port C to W\r\n\tandlw\tb'00000011'\t;mask all but RC0-1 (encoder)\r\n\tmovwf\tencold\t\t;preset encoder storage to current state\r\n\tmovwf\tencnew\r\n\tmovwf\telast\r\n\tmovwf\tetmp\r\n\tmovlw\tdcount\t\t;preset debounce\r\n\tmovwf\tencnt\r\n\t\t\r\n\tclrf\teetiml\t\t;stop eeprom timer\r\n\tclrf\teetimh\r\n\r\n\tdecf\tstdel,f\t\t;count down\r\n\r\n\tbanksel\tLATC\t\t;turn off all leds\r\n\tclrf\tLATC\r\n\tbanksel\t0\r\nsdel2\t\t\t\t;continue\r\n;-----------------------------------------------------------------\r\n#ifdef\trampup\t\t\t;if ramp-up speed enabled\r\n\tdecfsz\trampcnth,f\t;decrement divider\r\n\tgoto\tpoll\t\t;skip ramp if divider not 0\r\n\tmovlw\trampspdh\t;else reset divider\r\n\tmovwf\trampcnth\r\n\tdecfsz\trampcnt,f\r\n\tgoto\tpoll\t\t;only ramp when ramp counter reaches 0\r\n\r\n;skip ramp if tim=rtim\t\r\n\tmovf\trtiml,W\t\t;low byte equal?\r\n\txorwf\ttiml,W\r\n\tbtfss\tSTATUS,Z\r\n\tgoto\tramp0\t\t;no, may need ramp\r\n\t\r\n\tmovf\trtimh,W\t\t;yes, high byte equal?\r\n\txorwf\ttimh,W\r\n\tbtfsc\tSTATUS,Z\r\n\tgoto\tpoll\t\t;yes, skip ramp code if rtim=tim\r\n\r\nramp0\tmovlw\trampspd\t\t;not equal, may need ramp, reset ramp counter\r\n\tmovwf\trampcnt\r\n\r\n;see if updated speed is higher or lower, ramp up but not down\r\n\tmovf\trtiml,W\t\t;get ramp timer\r\n\tsubwf\ttiml,W\t\t;subtract from low byte\r\n\tmovf\trtimh,W\r\n\tsubwfb\ttimh,W\t\t;borrow to high byte\r\n\r\n\tbtfss\tSTATUS,C\t;carry set if rtim < tim\r\n\tgoto\tnoramp\t\t;don't ramp if equal or more (equal shouldn't get here)\r\n\t\r\n;new speed is faster so ramp up\r\n;--------------------------------------------------------\r\n;slow the up ramp rate at faster speeds 02\/22\/2016\r\n;rampcnt determines ramp speed - higher is slower\r\n;each left shift results in 1\/2 ramp speed\r\n\r\n\tmovf\trtimh,W\t\t;get hi speed byte in W\r\n\tandlw\t0xf0\t\t;mask low nibble\r\n\taddlw\t0x40\t\t;add for countdown, check for wrap\r\n\tbtfss\tSTATUS,C\t;slowdown not needed if no wrap\r\n\tgoto\tramp1\t\t;skip slowdown if not needed\r\n\t\r\n\tmovwf\tsptemp\t\t;here if ramp slowdown needed\r\n\tswapf\tsptemp,f\t;swap nibbles for loop counter\r\n\tincf\tsptemp,f\t;add 1 to count for decrement\r\n\t\r\nrmploop\t\t\t\t;loop for 1\/2-1\/16 speed\r\n\tlslf\trampcnt,f\t;shift for 1\/2 speed\r\n\tdecfsz\tsptemp,f\t;required number of times\r\n\tgoto\trmploop\t\t;loop for speed\/2 again\r\n;------------------------------------------------------------------\r\n;ramp-up routine\t\r\n;increment 16 bit time by 1\r\nramp1\r\n\tincfsz\trtiml,f\t\t;ramp up 1 if less\r\n\tgoto\tpoll\t\t;go polling if no hi byte incr\r\n\tincf\trtimh,f\t\t;increment hi byte if low byte wrapped\r\n\tgoto\tpoll\t\t;and go do polling\r\n\r\n#endif\t\t\t\t;end if ramp-up speed\r\n\r\n;------------------------------------------------------------------\r\n\t\r\n;here if new speed is lower - jump speed down\r\nnoramp\r\n\tmovf\ttiml,W\t\t;set fully ramped\r\n\tmovwf\trtiml\t\t;make sure rtim = tim\r\n\tmovf\ttimh,W\r\n\tmovwf\trtimh\r\n\r\n;start main polling routines\r\n;\t\r\n;polling includes:\r\n;1. servicing counters for encoder rotation speed detection and eeprom update timing\r\n;2. reading encoder\r\n;3. reading speed selector switch\r\n;4. updating speed data based on encoder readings for\r\n;   whichever of 3 speeds is selected\r\n\r\n;check our master .5mS counter (timer 0)\r\n\r\npoll\r\n\tbanksel\t0\r\n\tbtfss\tINTCON,TMR0IF\t;timer 0 overflow?\r\n\tgoto\tpoll_ramp\t;no loop\r\n\r\n;--------------------------------------------------------------------\r\n\r\n;each .5mS, service encoder rate and eeprom update counters\r\n\r\ntm0clr\t\t\t\t;yes, reset .5ms timer and update counters\r\n;set timing for 8 or 4 mHz xtal 11\/26\/2018\r\n#ifdef\tmhz8\t\t\t;for 8 mHz xtal\r\n\tmovlw\tb'11110000'\t;preset timer 0 to overflow in 16 prescale clocks\r\n#else\t\t\t\t;else for 4 mHz xtal\r\n\tmovlw\tb'11111000'\t;preset timer 0 to overflow in 8 prescale clocks\r\n#endif\r\n\tmovwf\tTMR0\t\t;for a total of .5mS\r\n\r\n\tbcf\tINTCON,TMR0IF\t;clear timer0 overflow for next time\r\n\t\r\n;increment counters - there are 2 counters.\r\n;\r\n;The first is a 16 bit counter for eeprom writing.\r\n;if its 0, its not incremented. When there's an encoder change\r\n;its set to a constant and then counts up for approx 20 sec.\r\n;Every encoder change presets it to the constant for about 20 sec.\r\n;so it starts over. When there's been no encoder change for\r\n;approx 20 sec, it wraps to 0, causes an eeprom write, and then\r\n;stays at 0 until there's another encoder change.\r\n;\r\n;The second counter determines speed of encoder rotation and\r\n;is used to specify step size for course and fine speed adjustment.\r\n;Every encoder change reads this counter value\r\n;and then resets it. When it reaches 0xff its held there to specify\r\n;the slow rotation. Not timed out causes one of 2 the faster adjustments\r\n;depending on the counter value. It should reach 0xff in approx. 32mS.\r\n;turning the encoder faster therefor results in faster changes per step.\r\n\r\n\tmovf\teetiml,W\t;running?\r\n\tiorwf\teetimh,W \t;not if 0x0000\r\n\tbtfss\tSTATUS,Z\t;Z set if not running\r\n\tgoto\tckee\r\n\r\n\tbanksel\t0\r\n\tgoto\tnoup\t \t;no eeprom update if counter not running\r\n\t\r\n;increment eeprom update counter, when it reaches 0:0\r\n;initiate an eeprom update write and then leave counter\r\n;at 0:0 until there's an encoder change\r\n\r\n;-------------------------------------------------\r\n\r\nckee\r\n\tbanksel\tLATC\r\n\tbsf\tLATC,RC5\t;turn on RC5 led\r\n\tbanksel\t0\r\n\r\n\tincfsz\teetiml,f \t;count up eeprom update counter\r\n\tgoto\tnoup\t  \t;if not 0 no increment hi, no eeprom update\r\n\tmovlw\t0x80\t\t;shorter delay time 03\/03\/2019\r\n\tiorwf\teetimh,f\r\n\tincfsz\teetimh,f\t;increment hi, timed out if 0\r\n\tgoto\tnoup\t\t;if not 0, not timed out. no eeprom update\r\n;------------------------------------------------------------\r\n;update eeprom data if eeprom update timer timed out\r\n\tcall\teeupd\t\t;if 0:0, update, stay at 0:0 till next encoder change\r\n\t\r\n\tbanksel\tLATC\r\n\tclrf\tLATC\t\t;turn off all leds when no pending write\r\n\tbanksel\t0\r\n\t\r\n;increment encoder speed timer,\r\nnoup\r\n\tincfsz\tenctim,f\t;increment encoder rotation timer\r\n\tgoto\tencrd\r\n\tdecf\tenctim,f\t;hold at 0xff if max time\r\n\r\n;-------------------------------------------------------------------\r\n\r\n;encoder service - flag if change, store speed and direction\r\n;restart eeprom write timer\r\n;12\/03\/2018 added debounce code\r\n\r\nencrd\r\n;--------------------------------------------------------------------------\r\n\tbanksel 0\r\n\tclrf\tencspd\t\t;preset set encoder speed at 0 for no change\r\n\tmovf\tPORTC,W\t \t;read port C to W\r\n\tandlw\tb'00000011'\t;mask all but RC0-1 (encoder)\r\n\tmovwf \tetmp\t\t;keep it to tmp\r\n\txorwf\telast,W\t\t;match last read?\r\n\tbtfsc\tSTATUS,Z\r\n\tgoto\ten0\t\t;yes, continue\r\n\r\n\tmovf\tetmp,W\t\t;no, update last read\r\n\tmovwf\telast\r\n\tmovlw\tdcount\t\t;restart debounce count\r\n\tmovwf\tencnt\r\n\tgoto\tcks2\t\t;and continue without encoder update\r\nen0\r\n\tdecfsz\tencnt,f\t\t;decrement debounce count\r\n\tgoto\tcks2\t\t;if not 0, continue without encoder update\r\n\r\n\tmovlw\tdcount\t\t;if 0, preset debounce count\r\n\tmovwf\tencnt\t\t;and process encoder\r\n;---------------------------------------------------------------------------\r\nencrd1\t\r\n\tmovf\tencnew,W\t;move the previous \"New\" value \r\n\tmovwf\tencold \t\t;to \"Old\" file register\r\n\tmovf\tetmp,W\t\t;then move the new value\r\n\tmovwf\tencnew\t\t;to \"New\" file register\r\n\t\r\n\txorwf\tencold,W \t;xor old and new\r\n\tbtfsc\tSTATUS,Z \t;xor=0=no change\r\n\tgoto\tcks2\t  \t;nothing changed, goto speed selector\r\n;----------------------------------------------------------------------\r\n;encoder changed, restart eeprom update timer on each change\r\nespd\r\n\tclrf\teetiml\r\n\tclrf\teetimh\r\n\tbsf\teetimh,7\t;preset eeprom update counter for approx 20 sec. count\r\n;----------------------------------------------------------------------\r\n;determine encoder rotation speed\r\n\tincfsz\tenctim,W\t;see if encoder timer timed out\r\n\tgoto\temed\t \t;no, set fast or med speed\r\neslow\r\n\tbanksel\tLATC\t\t;turn off RC4 led if slow\r\n\tbcf\tLATC,RC4\r\n\tbanksel\t0\r\n\tmovlw\te_slow\t\t;yes, get slow speed constant in W\r\n\tgoto\tckenc\r\nemed\r\n\tbanksel\tLATC\t\t;turn on RC4 led if not slow\r\n\tbsf\tLATC,RC4\r\n\tbanksel\t0\r\n\tsublw\t0x10\t\t;add 3rd speed 11\/20\/2018\r\n\tbtfsc\tSTATUS,C\t;encoder turning really fast?\r\n\tgoto\tefast\t\t;yes, go set fast speed\r\n\tmovlw\te_med\t\t;no, get med speed constant in W\r\n\tgoto\tckenc\r\nefast\r\n\tmovlw\te_fast\t\t;get fast speed constant in W\r\nckenc\r\n\tmovwf\tencspd\t\t;store encoder speed \r\n\tmovlw\tb'11000000'\t;reset encoder timer for 64 counts (32mS)\r\n\tmovwf\tenctim\r\n;-----------------------------------------------------------------------\r\n;get encoder direction\r\n\tclrf\tencdir\t\t;assume ccw direction\r\n\r\n\tmovf\tencold,W\t;move the right\r\n\tandlw\tb'00000001'\t;bit of the\r\n\tmovwf\tetmp2\t\t;OLD value to etmp2\r\n\t\r\n\tmovf\tencnew,W\t;move the left\r\n\tandlw\tb'00000010'\t;bit of the\r\n\tmovwf\tetmp\t\t;new value to etmp\r\n\t\r\n\tlsrf\tetmp,W\t\t;shift right one bit etmp\r\n\txorwf\tetmp2,W\t\t;xor them. if it is zero CCW else CW\r\n\tbtfss\tSTATUS,Z\t;leave at 0 if ccw (skip next)\r\n\tbsf\tencdir,1\t;else make it 1 if CW\r\n\r\n;end encoder service\r\n;---------------------------------------------------------------------\t\r\n\r\n;speed selection and update motor speed\r\n;read turntable speed selector switch\t\r\ncks2\r\n\tbanksel\t0\r\n\tbtfss\tPORTC,RC2\t;78 selected?\r\n\tgoto\ttim78\t\t;yes, do 78\r\n\tbtfsc\tPORTC,RC3\t;33 selected?\r\n\tgoto\ttim45\t\t;no, do 45\r\n\t\t\t\t;yes, fall into tim33\r\n\r\n;----------------------------------------------------------------\t\t\t\t\r\n\r\n;speed select routines\r\n;get constants for appropriate speed and update\r\n;speed data based on last encoder change if any\r\ntim33\r\n\tmovf\tspl33,W\t\t;pulse width for 33 rpm\r\n\tmovwf\ttiml\t\t;get current 33 constants\r\n\tmovf\tsph33,W\r\n\tmovwf\ttimh\r\n\tcall\ttimadj\t\t;adjust time by encoder data if changed\r\n\tmovf\ttiml,W\t\t;update speed data in ram\r\n\tmovwf\tspl33\r\n\tmovf\ttimh,w\r\n\tmovwf\tsph33\r\n\tgoto\tpoll_ramp\t;back to top of main loop\r\ntim45\r\n\tmovf\tspl45,W\t\t;pulse width for 45 rpm\r\n\tmovwf\ttiml\t\t;get current 45 constants\r\n\tmovf\tsph45,W\r\n\tmovwf\ttimh\r\n\tcall\ttimadj\t\t;adjust time by encoder data if changed\r\n\tmovf\ttiml,W\t\t;update speed data in ram\r\n\tmovwf\tspl45\r\n\tmovf\ttimh,w\r\n\tmovwf\tsph45\r\n\tgoto\tpoll_ramp\t;back to top of main loop\r\ntim78\r\n\tmovf\tspl78,W\t\t;pulse width for 78 rpm\r\n\tmovwf\ttiml\t\t;get current 78 constants\r\n\tmovf\tsph78,W\r\n\tmovwf\ttimh\t\t\r\n\tcall\ttimadj\t\t;adjust time by encoder data if changed\r\n\tmovf\ttiml,W\t\t;update speed data in ram\r\n\tmovwf\tspl78\r\n\tmovf\ttimh,W\r\n\tmovwf\tsph78\r\n\tgoto\tpoll_ramp\t;back to top of main loop\r\n\r\n;end of main polling loop\r\n\r\n;--------------------subroutines-------------------------------\r\n\t\r\n;subroutine to adjust speed data per encoder\r\n;returns with 16 bit adjusted data in timl, timh\r\n;just return doing nothing if no encoder change\r\ntimadj\r\n\tclrw\t\t\t;see if encoder speed adj value is 0\r\n\tiorwf\tencspd,W\t;if its 0, no encoder change\r\n\tbtfsc\tSTATUS,Z\r\n\treturn\t\t\t;do nothing if its 0, else continue\r\n\r\n\tbtfss\tencdir,1\t;get direction (1 is cw)\r\n\tgoto\teccw\t\t;skip if not cw\r\n\t\r\n;add if cw\r\necw\r\n\tmovf\tencspd,W\t;get speed adj\r\n\taddwf\ttiml,f\t\t;add to low byte\r\n\tclrw\r\n\taddwfc\ttimh,f\t\t;carry to high byte\r\n\t\r\n;check if over limit, set to limit if so\r\n\tmovlw\t(d'256'-hilim)\t;get high time limit (subtr from 256)\r\n\taddwf\ttimh,W\t\t;add to time high byte\r\n\tbtfss\tSTATUS,C\t;over?\r\n\tgoto\thok\t\t;no leave alone\r\n\tmovlw\thilim\t\t;yes, set at high limit\r\n\tmovwf\ttimh\r\n\tclrf\ttiml\r\nhok\r\n\tclrf\tencspd\t\t;zero out enc speed so we don't update again\r\n\treturn\r\n\r\n\t;subtract if ccw\r\neccw\r\n\tmovf\tencspd,W\t;get speed adj\r\n\tsubwf\ttiml,f\t\t;subtract from to low byte\r\n\tclrw\r\n\tsubwfb\ttimh,f\t\t;borrow to high byte\r\n\r\n;check if under limit, set to limit if so\r\n\tmovlw\tlolim\t\t;get low time limit\r\n\tsubwf\ttimh,W\t\t;subtract from time low byte\r\n\tbtfsc\tSTATUS,C\t;under?\r\n\tgoto\tlok\t\t;no leave alone\r\n\tmovlw\tlolim\t\t;yes, set at low limit\r\n\tmovwf\ttimh\r\n\tclrf\ttiml\r\nlok\r\n\tclrf\tencspd\t\t;zero out enc speed so we don't update again\r\n\treturn\r\n\r\n;---------------------------------------------------\r\n\r\n;subroutine to update eeprom speed data\r\n;do this after encoder activity followed\r\n;by 20 sec of inactivity\r\n\r\n;read eeprom and compare to current setting\r\n;only update what's changed\r\n\r\neeupd\r\n\tmovlw\tel33\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;33 low byte\r\n\tcall\teeread\r\n\txorwf\tspl33,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\tgoto\txh33\r\n\tmovf\tspl33,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\nxh33\r\n\tmovlw\teh33\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;33 high byte\r\n\tcall\teeread\r\n\txorwf\tsph33,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\tgoto\txl45\r\n\tmovf\tsph33,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\nxl45\r\n\tmovlw\tel45\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;45 low byte\r\n\tcall\teeread\r\n\txorwf\tspl45,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\tgoto\txh45\r\n\tmovf\tspl45,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\nxh45\r\n\tmovlw\teh45\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;45 high byte\r\n\tcall\teeread\r\n\txorwf\tsph45,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\tgoto\txl78\r\n\tmovf\tsph45,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\nxl78\r\n\tmovlw\tel78\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;78 low byte\r\n\tcall\teeread\r\n\txorwf\tspl78,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\tgoto\txh78\r\n\tmovf\tspl78,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\nxh78\r\n\tmovlw\teh78\t\t;get eeprom data in W\r\n\tmovwf\tee_addr\t\t;78 high byte\r\n\tcall\teeread\r\n\txorwf\tsph78,W\t\t;changed?\r\n\tbtfsc\tSTATUS,Z\t\r\n\treturn\t\t\t;no, we're done, return\r\n\tmovf\tsph78,W\t\t;yes, write new\r\n\tcall\teewrt\r\n\r\n\treturn\t\t\t;and return, all updates done\r\n\r\n;----------------------------------------------------------------\r\n;eeprom read and write subroutines\r\n\r\n;subroutine for EEPROM write\r\n;address in ee_addr, byte to write in W\r\neewrt\r\n\tbanksel\tEEDATL\r\n\tmovwf\tEEDATL\t\t;Data Memory Value to write in W\r\n\r\n\tbanksel\t0\r\n\tmovf\tee_addr,W\t;get address to write\r\n\tbanksel\tEEADRL\r\n\tmovwf\tEEADRL\t\t;Data Memory Address to write\r\n\t\r\n\tbcf\tEECON1,CFGS\t;Deselect Configuration space\r\n\tbcf\tEECON1,EEPGD\t;Point to DATA memory\r\n\tbsf\tEECON1,WREN\t;Enable writes\r\n\r\n\tbcf\tINTCON,GIE\t;Disable Interrupts.\r\n\tmovlw\t0x55\r\n\tmovwf\tEECON2\t\t;Write 55h\r\n\tmovlw\t0xAA\r\n\tmovwf\tEECON2\t\t;Write AAh\r\n\tbsf\tEECON1,WR\t;Set WR bit to begin write\r\n\tbsf\tINTCON,GIE\t;Enable Interrupts\r\n\tbcf\tEECON1,WREN\t;Disable further writes\r\n\r\neewait\r\n\tbtfsc\tEECON1,WR\t;Wait for write to complete\r\n\tgoto\teewait\r\n\r\n\tbanksel\t0\t\t;done, back to bank 0 default\r\n\treturn\t\t\t;and return after write\r\n\t\r\n;subroutine for EEPROM read\r\n;Address to read is in eeaddrl\r\n;returns with value in W\r\n;there are 2 entry points, the first with the addr to read\r\n;in ee_addr, the second with the addr to read in W\r\neeread\t\t\t\t;enter here with addr in ee_addr\r\n\tmovf\tee_addr,W\t;get addr to read in W\r\neerd1\t\t\t\t;enter here with addr in W\r\n\tbanksel\tEEADRL\t\t;or start here with addr to read in W\r\n\tmovwf\tEEADRL\t\t;set addr to read\r\n\t\r\n\tbcf\tEECON1,CFGS\t;Deselect Config space\r\n\tbcf\tEECON1,EEPGD\t;Point to DATA memory\r\n\tbsf\tEECON1,RD\t;EE Read\r\n\tmovf\tEEDATL,W\t;W = read data\r\n\t\r\n\tbanksel\t0\t\t;back to bank 0 default\r\n\treturn\t\t\t;return with requested data in W\r\n\t\r\n\tEND\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>; motor servo program for 16f1823 ; Bill Thompson &#8211; 03-29-2015 ; ; Revision 2 &#8211; Fall 2018 ; 09\/29\/2018 many changes to use internal comparator ; 10\/23\/2018 first running version with internal comparator ; 10\/28\/2018 code cleanup, no functional changes ; 10\/30\/2018 enable brownout reset &#038; watchdog timer ; 11\/15\/2018 reorder interrupt logic for [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[14],"tags":[],"class_list":["post-883","post","type-post","status-publish","format-standard","hentry","category-projects-b"],"_links":{"self":[{"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/posts\/883","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/comments?post=883"}],"version-history":[{"count":3,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/posts\/883\/revisions"}],"predecessor-version":[{"id":1616,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/posts\/883\/revisions\/1616"}],"wp:attachment":[{"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/media?parent=883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/categories?post=883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/billthompson.us\/bt\/wp-json\/wp\/v2\/tags?post=883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}