UP / Down Counter Using Rotary Encoder

Discussion in 'Embedded Systems and Microcontrollers' started by MCU88, Mar 15, 2015.

  1. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    Hello...

    I am working on this:

    [​IMG]

    I want to save the current display number in EEPROM so that when power is removed and reapplied it will restore to this number. Pressing the rotary encoder to reset the count.

    With everything that I have tried doing the display flickers when incrementing or decrementing the count using the rotary encoder because of the 5mS of time required to write to the EEPROM. So I have tried writing on-the-fly i.e each time the display number changes it gets written to memory.

    Any suggestions? Much appreciated!

    My code is here:
    Code (Text):
    1.  
    2. unsigned short i = 0;
    3. unsigned short j = 0;
    4. unsigned short scan = 0;
    5. unsigned short rLeft = 0;
    6. unsigned short rRight = 0;
    7. unsigned short rotating = 0;
    8. signed short ones = 0;
    9. signed short tens = 0;
    10. signed short hundreds = 0;
    11. signed short thousands = 0;
    12.  
    13. // :: Defines :: //
    14.  
    15. // Displays ...
    16. #define dslpD PORTA.F1
    17. #define dslpC PORTA.F0
    18. #define dslpB PORTA.F3
    19. #define dslpA PORTA.F2
    20.  
    21. // Segments ...
    22. #define segA PORTB.F7
    23. #define segB PORTB.F5
    24. #define segC PORTB.F3
    25. #define segD PORTB.F2
    26. #define segE PORTB.F1
    27. #define segF PORTB.F6
    28. #define segG PORTB.F4
    29.  
    30. #define rotary02 PORTB.F0
    31. #define rotary01 PORTA.F4
    32. #define rotary03 PORTB.F3
    33.  
    34. void num0()
    35. {
    36.   segA = 1;
    37.   segB = 1;
    38.   segC = 1;
    39.   segD = 1;
    40.   segE = 1;
    41.   segF = 1;
    42. }
    43.  
    44. void num1()
    45. {
    46.   segB = 1;
    47.   segC = 1;
    48. }
    49.  
    50. void num2()
    51. {
    52.   segA = 1;
    53.   segB = 1;
    54.   segG = 1;
    55.   segD = 1;
    56.   segE = 1;
    57. }
    58.  
    59. void num3()
    60. {
    61.   segA = 1;
    62.   segB = 1;
    63.   segC = 1;
    64.   segD = 1;
    65.   segG = 1;
    66. }
    67.  
    68. void num4()
    69. {
    70.   segF = 1;
    71.   segG = 1;
    72.   segB = 1;
    73.   segC = 1;
    74. }
    75.  
    76. void num5()
    77. {
    78.   segA = 1;
    79.   segF = 1;
    80.   segG = 1;
    81.   segC = 1;
    82.   segD = 1;
    83. }
    84.  
    85. void num6()
    86. {
    87.   segA = 1;
    88.   segC = 1;
    89.   segD = 1;
    90.   segE = 1;
    91.   segF = 1;
    92.   segG = 1;
    93. }
    94.  
    95. void num7()
    96. {
    97.   segA = 1;
    98.   segB = 1;
    99.   segC = 1;
    100. }
    101.  
    102. void num8()
    103. {
    104.   segA = 1;
    105.   segB = 1;
    106.   segC = 1;
    107.   segD = 1;
    108.   segE = 1;
    109.   segF = 1;
    110.   segG = 1;
    111. }
    112.  
    113. void num9()
    114. {
    115.   segA = 1;
    116.   segB = 1;
    117.   segC = 1;
    118.   segF = 1;
    119.   segG = 1;
    120. }
    121.  
    122. void blankDigit()
    123. {
    124.   segA = 0;
    125.   segB = 0;
    126.   segC = 0;
    127.   segD = 0;
    128.   segE = 0;
    129.   segF = 0;
    130.   segG = 0;
    131. }
    132.  
    133. void displayOff()
    134. {
    135.    dslpA = 0;
    136.    dslpB = 0;
    137.    dslpC = 0;
    138.    dslpD = 0;
    139. }
    140.  
    141. void decCount()
    142. {
    143.    if (ones != 0 || tens != 0 || hundreds != 0 || thousands != 0)
    144.    {
    145.       ones--;
    146.  
    147.       if (ones < 0)
    148.       {
    149.          if (tens > 0)
    150.          {
    151.             ones = 9;
    152.             tens --;
    153.          }
    154.          else if (hundreds > 0)
    155.          {
    156.             if (tens == 0)
    157.             {
    158.                tens = 9;
    159.                ones = 9;
    160.                hundreds --;
    161.             }
    162.          }
    163.          else if (thousands > 0)
    164.          {
    165.             if (hundreds == 0)
    166.             {
    167.                if (tens == 0)
    168.                {
    169.                   tens = 9;
    170.                   ones = 9;
    171.                   hundreds = 9;
    172.                   thousands --;
    173.                }
    174.             }
    175.          }
    176.       }
    177.    }
    178. }
    179.  
    180. void incCount()
    181. {
    182.    if (ones != 9 || tens != 9 || hundreds != 9 || thousands != 9)
    183.    {
    184.       ones++;
    185.  
    186.       if (ones > 9)
    187.       {
    188.          ones = 0;
    189.          tens ++;
    190.       }
    191.       if (tens > 9)
    192.       {
    193.          tens = 0;
    194.          hundreds ++;
    195.       }
    196.       if (hundreds > 9)
    197.       {
    198.          hundreds = 0;
    199.          thousands ++;
    200.       }
    201.    }
    202. }
    203.  
    204. void rstCount()
    205. {
    206.    ones = 0;
    207.    tens = 0;
    208.    hundreds = 0;
    209.    thousands = 0;
    210. }
    211.  
    212. void pollRotaryEncoder()
    213. {
    214.    TRISB = 0x09;
    215.    
    216.    if (rotary03 == 0)
    217.    {
    218.       rstCount();
    219.    }
    220.  
    221.    TRISB = 0x01;
    222.  
    223.    if (rotary01 == 0)
    224.    {
    225.       if (rotary02 == 0)
    226.       {
    227.          rotating = 1;
    228.       }
    229.    }
    230.  
    231.    if (rotating == 1)
    232.    {
    233.       if (rotary01 == 1)
    234.       {
    235.          rRight = 1;
    236.          rotating = 0;
    237.       }
    238.       if (rotary02 == 1)
    239.       {
    240.          rLeft = 1;
    241.          rotating = 0;
    242.       }
    243.    }
    244.  
    245.    if (rotary01 == 1)
    246.    {
    247.       if (rotary02 == 1)
    248.       {
    249.          if (rleft == 1)
    250.          {
    251.             incCount();
    252.             rLeft = 0;
    253.          }
    254.            
    255.          if (rRight == 1)
    256.          {
    257.             decCount();
    258.             rRight = 0;
    259.          }
    260.       }
    261.    }
    262. }
    263.  
    264. void setDigit(signed short digit)
    265. {
    266.    switch (digit)
    267.    {
    268.       case 0:
    269.          num0();
    270.          break;
    271.  
    272.       case 1:
    273.          num1();
    274.          break;
    275.  
    276.       case 2:
    277.          num2();
    278.          break;
    279.  
    280.       case 3:
    281.          num3();
    282.          break;
    283.  
    284.       case 4:
    285.          num4();
    286.          break;
    287.  
    288.       case 5:
    289.          num5();
    290.          break;
    291.  
    292.       case 6:
    293.          num6();
    294.          break;
    295.  
    296.       case 7:
    297.          num7();
    298.          break;
    299.  
    300.       case 8:
    301.          num8();
    302.          break;
    303.  
    304.       case 9:
    305.          num9();
    306.          break;
    307.          
    308.       case 10:
    309.          blankDigit();
    310.          break;
    311.  
    312.    }
    313. }
    314.  
    315. void multiplexDisplays()
    316. {
    317.   displayOff();
    318.   blankDigit();
    319.          
    320.    switch (scan)
    321.    {
    322.       case 0:
    323.          setDigit(ones);
    324.          dslpA = 1;
    325.          break;
    326.  
    327.       case 1:
    328.          setDigit(tens);
    329.          dslpB = 1;
    330.          break;
    331.  
    332.       case 2:
    333.          setDigit(hundreds);
    334.          dslpC = 1;
    335.          break;
    336.  
    337.       case 3:
    338.          setDigit(thousands);
    339.          dslpD = 1;
    340.          break;
    341.    }
    342.  
    343.    scan++;
    344.  
    345.    if (scan == 4)
    346.    {
    347.       scan = 0;
    348.    }
    349. }
    350.  
    351. void main()
    352. {
    353.   CMCON = 7;            // Disable analog comparators
    354.   TRISA = 0x10;         // PortA as output ...
    355.   TRISB = 0x01;         // 4 inputs & 4 outputs
    356.   PORTA = 0x00;         // Init port, all pins low
    357.   PORTB = 0x00;         // Init port, all pins low
    358.  
    359.   Delay_ms(500);
    360.   // :: Infinite program loop :: //
    361.  
    362.   while(1)
    363.   {
    364.      multiplexDisplays();
    365.      Delay_us(100);
    366.      pollRotaryEncoder();
    367.   }
    368. }
    369.  
     
  2. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    Probably the easiest thing to do is dump the display multiplex code into an ISR so it just runs all the time and the EEPROM delay does not interfere with the updates.

    I do hope you've checked how many write cycles your EEPROM has. Seems to me like you will overtax the thing fairly quickly doing all those writes to it.
     
  3. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    What is an ISR? And can you elaborate on how to implement it?

    What if I were to somehow detect when the circuit is powered down and then just do one write to the EEPROM, somehow but with no power, no that won't work...

    Yes indeed that is the other problem as you say write cycle life of the EEPROM.
     
  4. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    ISR: Interrupt Service Routine. Try Google.

    I have no idea how you could implement this as your device and compiler are still mysteries.

    A power fail signal isn't rocket science. Basically all you need is a cap to supply 5mS (plus) of power to the device, a diode to isolate it from the rest of the unit, and an input pin to ping when power is lost. This ping pin needs be have an interrupt function as this event needs top priority handling.
     
    MCU88 likes this.
  5. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    Yeah thanks for that. It makes sense what you say about the:

    a. Cap to discharge power for 5mS EEPROM write
    b. Diode to isolate 1n914
    c. Pin to detect logic low

    I am using the mikroC compiler and an PIC16F628 MCU.
     
  6. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,676
    899
    What chip are you using?

    The write time per byte of the 16F628A is 4 mS nominal, 8 mS max. The minimum write voltage is 4.5V. Without knowing the current the circuit draws, an exact calculation cannot be made. Assuming that is 25 mA, you will need a fairly large capacitor (roughly 500 to 1000 uF) to supply current after disconnecting the battery.

    Why not use an on/off switch, like a push button? Then you can detect the switch closure and shut the system down in an orderly fashion while retaining the last value. Startup wouldbe the converse and also done in an orderly manner.

    John

    Edit: The 4 mS was from a Microchip forum. The datasheet gives 4 mS for block erase and 2 to 4 mS for a single write. It will still require a fairly large capacitor.
     
    Last edited: Mar 15, 2015
  7. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    Put the PIC in sleep mode when it is off? Yeah I guess this is another way of doing it. Many ways to skin a cat ;)
     
  8. jpanhalt

    AAC Fanatic!

    Jan 18, 2008
    5,676
    899
    Sorry for the multiple edits. So, instead of making others suffer my misreading, here is the relevant timing table ( I misread the program flash values as EEPROM:

    upload_2015-3-15_10-6-13.png

    As for sleep when it is off, how will you turn it "off?" And yes, sleep would save current and retain your value.

    John
     
  9. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    Be nice to find a way that does not involve me modifying the PCB for additional parts. I currently have an slide switch as the power switch for the circuit.

    Keep your ideas coming. Thanx!
     
  10. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    This is why we complete the design before going to a PCB ;-)

    OK so for a no parts change solution... If you are twiddling the knob things are changing so no need to keep each and every value, just need them to be saved when you are done changing things.

    So reset a timer every time the know changes, then wait something "reasonable" (on the order of a few seconds) and if the know is not changes then store the new value.

    May not catch every final value but should be close most of the time.
     
    JWHassler likes this.
  11. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    I prototype straight to PCB now. I used to veroboard my projects first, but I can pop out an single sided board in my garage in under 30-minutes. I never get a project right the first time.

    May not catch every time? Well engineering is all about compromise. The user would still see the display flicker every time the timer function wrote to the EEPROM. So I guess this would be the compromise.
     
  12. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    Wish I could get out a board like that. My stuff can't fit on a single sided board so I rely on solderable protoboards and wire.

    There's no reason the display should flicker when updating the EEPROM.

    Loosing power while writing to the EEPROM is an inherent problem with this design no matter when you save the last value. Loss of power during the write leads to corrupted data.

    BTW there is no need to hold up the display, just the PIC controller.
     
  13. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    I like drawing boards. Kinda like an jigsaw puzzle, especially with single-sided boards that require multiple wire links.

    Well that is another problem. I agree that the data would become corrupted if the MCU was in the middle of an EEPROM write and power was lost. I am leaning towards having a power fail schema with the capacitor, diode and detect pin transition from logic states high to low then write to EEPROM.
     
  14. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    So something like this?

    [​IMG]
     
  15. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    Problem though is that the diode will conduct when the supply falls 0.6V below what the cap has charged to, and the pin to detect the power fail will have to see <27% of VDD (standard CMOS specs 27% for logic low) -- I cannot see how this will work now!
     
  16. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    OK, looks like there is no min spec for EEPROM write so equate that with the PIC operating range down to 2V, the PIC itself takes some 3mA running at 20M, and we need to keep things going so 10mS minimum... so that means a cap of: 3mA * 10mS / (5V-2V) or 10 uF. That's a min, I'd use lots more.

    As long as you can separate the PIC power from everything else (do check sneaky paths) the power sense/holdup need not be more complicated than this:

    [​IMG]
     
  17. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    I am not sure how to add this to the schematic. As in to just power the MCU and not the peripherals hanging off it.

    Can someone add the schema to this schematic:

    [​IMG]
     
  18. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    The diode and resistor go between the +5V line and pin 14 of the PIC. Pin 14 is the PIC PWR pin. The only spare pin you have is RA4.

    RA4 has no direct interrupt ability by itself as some RB pins have. So you can either poll the pin (test it very often) to see if power was lost, or use the other function of RA4 to be a clock for timer 0. If timer 0 is loaded with a value of 255 the next clock will cause a roll over which can generate an interrupt. That's a bit of finicky code but should work.

    OR... just put in your overall device spec "power must remain on for 1o seconds minimum after changing rotary knob to properly save setting. Failure to observe this limit may corrupt the stored value."
     
  19. MCU88

    Thread Starter Member

    Mar 12, 2015
    360
    35
    I could sleep at night with that. There are guys on the net selling electric superchargers for your car. What a load of rubbish! Complete rip off. eBay allow them to operate in business too !!!
     
Loading...