Advertisement
Francoo

74HC595 7-Seg Driver with Plus

Jan 30th, 2014
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.33 KB | None | 0 0
  1. #include <main.h>
  2.  
  3. /*
  4. STILL WORK IN PROGRESS!!!
  5. This code will be later mixed with the stepper driver code, as it's supposed to be a driver for a
  6. motorized equatorial mount. Every function will be kept, particularly those related to the 7-seg
  7. display driver.
  8. ### Description:
  9. This code allows you to drive up to 8 digits 7-segments display with two 74HC595 (shift register
  10. with tristate output and latch). Tested only with 4 digits, but, asides the BCD converter, it
  11. should work flawlessly with minor modifications. On default, the first register controls each
  12. segment and the second register controls the digit to show, but that can be also easily modified.
  13. Tested on a PIC16F628A, running at 4MHz (internal RC oscillator). Compiled with CCS.
  14. Author: Franco Sauvisky
  15.  
  16. ### Operation and tips:
  17. The fully encoded number has to be stored in a 4-bytes array, and it will switch between each
  18. single digit every time you call the function update_disp(). If time is required between switching
  19. digits - for example, to process some data - it's recommended to shutdown the display temporarily
  20. so it won't outshine a single digit (keep in mind that, after the function is called, it will keep
  21. that digit "on"). That might be done by setting the OE (output enable) pin or by calling the
  22. function clear_disp(), that sends two null bytes to the 74HC595 turning every output low. The same
  23. function might be also used for synchronizing the register when not using the MR (Master Reset)
  24. pin, though usually flushing any data will do the same.
  25.  
  26. The default mapping of segments for each bit is: byte = [A] [B] [C] [D] [E] [F] [G] [DT].
  27.  
  28. Some functions uses the fast_io directive. It only tells the compiler to don't change the tris
  29. value every time, so it can operate faster. The biggest problem is that you can only set up it for
  30. whole ports, so, when using different pins from the default, you might need to change it or add
  31. another one.
  32. It should cause no difference in other parts of the code, since it will return to standard_io on
  33. the end of the function, though you should not use the same pins for input, or let the tris go
  34. high, as that won't let the microcontroller modify those pins. If wanted, it can be removed without
  35. any hiss, although that will make the process slower.
  36.  
  37. There are two main modes of operation. You can refresh the full display (by calling update_disp()
  38. multiple times) and then do the data processing (previously turning off the display so every digit
  39. will be evenly bright) or you might assign an interrupt to a timer, in a manner that it will
  40. refresh the display automatically. That allows you to run any code freely, only using load_num()
  41. for modifying the display, and it's guaranteed that the brightness will keep constant and equal
  42. between digits. The cons of that method is the longer execution time, as it will be constantly
  43. interrupted for some time.
  44.  
  45. ### Functions:
  46.  
  47. - sreg_init(): Resets the 74HC595, enables output and set every pin low (but MR).
  48.  
  49. - sreg_send_long(int8 low, int8 high): Sends two int8 and clocks latch.
  50.  
  51. - sreg_clear(): Sends 0x0000, setting every 74HC595 output low. Needed when not using OE and/or
  52. MR pins.
  53.  
  54. - load_num(int16 numb, int dots, int *coded): Converts a int16 into a 7-segments A-G coded
  55. 4 elements array, plus decimal points.
  56.  
  57. - update_disp(int *encoded): Switches to the next character in enc7seg array. Needs to be
  58. called once for each digit. The digit bits are inverted, that is, the "on" digit will be a low
  59. output, though is simple to modify that behavior. The digit counter is internal (static variable).
  60.  
  61. - update_disp_but(int *encoded, int pcount): Same function than previously, though it allows you
  62. to choose manually which digit to display. It might be used for scanning buttons. On default, it
  63. will output the digit selector nibble twice, using some pins for button scanning and others for
  64. selecting digit, but I don't see why you wouldn't be able to use the same (allowing you to drive
  65. eight digits plus scanning up to eight buttons on the same pins).
  66.  
  67. - copy_arr(int *dest, int *src): Copies 4 bytes of src to dest. Useful when using presets for
  68. displaying texts and symbols, though memcpy() does the same (and can work with constant arrays,
  69. which this one can't). The biggest benefit is the low memory used.
  70.  
  71. - wait_for_button_press(int *buttons): Waits for any bit setting modification (i.e. positive edge)
  72. on *buttons and returns which button/bit has been pressed/set.
  73.  
  74. - write_play() (not used): Writes "play" on the display. Sample code for testing non-numeric
  75. data and manually setting each segment.
  76.  
  77. ### Global Variables:
  78.  
  79. - seven_seg_map[] (constant): Lookup table for BCD -> 7-segments conversion.
  80.  
  81. - one_bit_shift[] (constant): This array is used as a lookup table for situations where is needed
  82. to test a single bit quickly, as the ALU shifter usually takes some time (because it only can shift
  83. one position per time, needing multiple shifts for testing bits farthest from LSB).
  84. */
  85.  
  86. #define sh_cp pin_b7 // Shift Register Clock (shift on low -> high)
  87. #define st_cp pin_b6 // Storage Register (Latch) Clock (low -> high)
  88. #define ds_in pin_b5 // Serial Input
  89. #define mr_in pin_b4 // Master Reset (reseted low, not critical)
  90. #define oe_in pin_b2 // Output Enable (enabled low, not critical)
  91.  
  92. #define button pin_b0 // Button Input
  93.  
  94. void sreg_init();
  95. void sreg_send_long(int8 low, int8 high);
  96. void sreg_clear();
  97. void load_num(int16 numb, int dots, int *coded);
  98. void update_disp(int *encoded);
  99. void update_disp_button(int *encoded, int pcount);
  100. void copy_arr(int *dest, int32 src);
  101. int wait_for_button_press(int *buttons);
  102. int16 number_input(int16 stnumber, int *buttons_status, int *codpointer);
  103. void write_play();
  104. void motor_ustep_pwm(int steps, int count);
  105.  
  106. const int seven_seg_map[] = { 0xFC, 0x60 0xDA 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6, 0x00 };
  107. const int one_bit_shift[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
  108. const int16 power_ten[] = { 1, 10, 100, 1000, 10000 }; // Constant for selecting which digit to
  109. // increase or decrease.
  110.  
  111. // ########################## CODE FOR TESTING PURPOSE ONLY (DEMO) ##########################
  112. // [OLD] This demo was compiled on CCS (PCM) and ran on a PIC16F628A. It is supposed to use a 4
  113. // digits display, allowing you to modify a number by selecting a digit with two buttons and
  114. // increasing or decreasing it with another two buttons. Though the clocking frequency is low and
  115. // much of the time is keep updating the display, it feels very intuitive and responsive.[/OLD]
  116. // # MODIFIED: Now it's supposed to be a driver for an motorized equatorial mount, configurable.
  117.  
  118. const int preset_run[] = { 0x00, 0x2A, 0x38, 0x0A }; // Presets for the display
  119. const int preset_inte[] = { 0x9E, 0x1E, 0x2A, 0x20 };
  120. const int preset_step[] = { 0xCE, 0x9E, 0x1E, 0xB6 };
  121. const int preset_erro[] = { 0xFC, 0x0A, 0x0A, 0x9E };
  122. const int preset_rset[] = { 0x1E, 0x9E, 0xB6, 0x0A };
  123.  
  124. int codednum[4];
  125. int buttons=0;
  126.  
  127. #priority int_timer1, int_timer2, int_ext
  128.  
  129. #int_timer2
  130. void tmr2int()
  131. {
  132.    static int tcount;
  133.    update_disp_button(codednum,tcount);
  134.    if(!input(button))
  135.    {
  136.       buttons |= one_bit_shift[tcount];
  137.    }
  138.    else
  139.    {
  140.       buttons &= ~one_bit_shift[tcount];
  141.    }
  142.    tcount = (tcount+1) % 4;
  143. }
  144.  
  145. #int_timer1
  146. void tmr1int()
  147. {
  148.    static int pwmcount=0;
  149.    pwmcount=(pwmcount+1) % 64;
  150.    motor_ustep_pwm(8,pwmcount);
  151. }
  152.  
  153. // Button bit map:
  154. // 1 and 2: Left/Decrease and Right/Increase
  155. // 3: Set/Go/Play
  156. // 4: Back
  157.  
  158. void main()
  159. {  
  160.    sreg_init();
  161.    port_b_pullups(true); // Pull-up for button scanning.
  162.    setup_timer_2(t2_div_by_16,150,1); // On a 4MHz clock, it changes digit every 24ms.
  163.    
  164.    enable_interrupts(global);
  165.    enable_interrupts(int_timer2);
  166.    
  167.    int menuitem=0;
  168.    int tempb;
  169.    int ustep=read_eeprom(0x00); // Load last settings
  170.    int16 intval=~read_eeprom(0x01);
  171.    intval<<=8;
  172.    intval|=~read_eeprom(0x02);
  173.    int pwmcount;
  174.    
  175.    
  176.    while(true)
  177.    {
  178.       switch(menuitem)
  179.       {
  180.          case 0:
  181.             memcpy(codednum,preset_run,4);
  182.             tempb=wait_for_button_press(&buttons);
  183.             if(tempb==4)
  184.             {
  185.                delay_ms(30);
  186.                disable_interrupts(int_timer2);
  187.                
  188.                sreg_clear();
  189.                output_a(0x00);
  190.                
  191.                setup_timer_2(t2_div_by_1,255,1);
  192.                setup_ccp1(ccp_pwm);
  193.                
  194.                setup_timer_1(t1_internal | t1_div_by_4);
  195.                enable_interrupts(int_timer1);
  196.                
  197.                if(input(pin_b0)) while(!input(pin_b0));
  198.                while(input(pin_b0));
  199.                
  200.                disable_interrupts(int_timer1);
  201.                setup_ccp1(ccp_off);
  202.                setup_timer_2(t2_div_by_16,150,1);
  203.                enable_interrupts(int_timer2);
  204.             }
  205.             break;
  206.          case 1:
  207.             memcpy(codednum,preset_inte,4);
  208.             tempb=wait_for_button_press(&buttons);
  209.             if(tempb==4) intval=number_input(intval, &buttons, codednum);
  210.             write_eeprom(0x01,~intval>>8);
  211.             write_eeprom(0x02,~intval);
  212.             break;
  213.          case 2:
  214.             memcpy(codednum,preset_step,4);
  215.             tempb=wait_for_button_press(&buttons);
  216.             break;
  217.          case 3:
  218.             memcpy(codednum,preset_rset,4);
  219.             tempb=wait_for_button_press(&buttons);
  220.             if(tempb==4)
  221.             {
  222.                write_eeprom(0x00,0xFF);
  223.                write_eeprom(0x01,0xFF);
  224.                write_eeprom(0x02,0xFF);
  225.                reset_cpu();
  226.             }
  227.             break;
  228.       }
  229.       if(tempb==2)
  230.       {
  231.          menuitem=(menuitem+1) % 4;
  232.       }
  233.       else if(tempb==1)
  234.       {
  235.          menuitem=(menuitem+3) % 4;
  236.       }
  237.    }
  238. }
  239.  
  240. // ################################## END OF TESTING CODE ##################################
  241. // -----------------------------------------------------------------------------------------
  242. // ############################# DISPLAY CONTROLLER FUNCTIONS #############################
  243.  
  244. void sreg_init()
  245. {
  246.    output_low(sh_cp);
  247.    output_low(ds_in);
  248.    output_low(mr_in);
  249.    output_high(mr_in);
  250.    output_high(st_cp);
  251.    output_low(st_cp);
  252.    output_low(oe_in);
  253. }
  254.  
  255. void sreg_send_long(int8 high, int8 low)
  256. {
  257.    #use fast_io(B) // Change, add or remove if using another pins.
  258.    output_low(sh_cp);
  259.    for(int i=0; i<8; i++)
  260.    {
  261.       output_bit(ds_in,high&0x80);
  262.       output_high(sh_cp);
  263.       output_low(sh_cp);
  264.       high<<=1;
  265.    }
  266.    for(i=0; i<8; i++)
  267.    {
  268.       output_bit(ds_in,low&0x80);
  269.       output_high(sh_cp);
  270.       output_low(sh_cp);
  271.       low<<=1;
  272.    }
  273.    output_low(st_cp);
  274.    output_high(st_cp);
  275.    output_low(st_cp);
  276.    #use standard_io(B) // This also.
  277. }
  278.  
  279. void sreg_clear()
  280. {
  281.    output_low(sh_cp);
  282.    for(int i=0; i<16; i++)
  283.    {
  284.       output_high(sh_cp);
  285.       output_low(sh_cp);
  286.    }
  287.    output_low(st_cp);
  288.    output_high(st_cp);
  289.    output_low(st_cp);
  290. }
  291.  
  292. void load_num(int16 numb, int dots, int *coded)
  293. {
  294.    int bcd_digit;
  295.    for(int i=0; i<4; i++)
  296.    {
  297.       bcd_digit = (numb % 10);
  298.       numb /= 10;
  299.       coded[i]=seven_seg_map[bcd_digit]|(bit_test(dots,i));
  300.    }
  301. }
  302.  
  303. void update_disp(int *encoded)
  304. {
  305.    static int count;
  306.    sreg_send_long(~one_bit_shift[count],encoded[count]);
  307.    count=(count+1)&4;
  308. }
  309.  
  310. void update_disp_button(int *encoded, int pcount)
  311. {
  312.    int digsel=~(one_bit_shift[pcount]+(one_bit_shift[pcount]<<4));
  313.    sreg_send_long(digsel,encoded[pcount]);
  314. }
  315.  
  316. void copy_arr(int *dest, int *src)
  317. {
  318.    dest[0]=src[0];
  319.    dest[1]=src[1];
  320.    dest[2]=src[2];
  321.    dest[3]=src[3];
  322. }
  323.  
  324. int wait_for_button_press(int *buttons_status)
  325. {
  326.    int temp;
  327.    int start;
  328.    
  329.    do
  330.    {
  331.       start=*buttons_status;
  332.       while(*buttons_status==start);
  333.       temp = (*buttons_status^start)&~start;
  334.    } while(temp==0);
  335.    
  336.    return temp;
  337. }
  338.  
  339. int16 number_input(int16 stnumber, int *buttons_status, int *codpointer)
  340. {
  341.    int pos=3;
  342.    int16 tempn=0;
  343.    int1 returnflag=0;
  344.    load_num(stnumber,one_bit_shift[pos],codpointer);
  345.    
  346.    while(!returnflag)
  347.    {
  348.       switch(wait_for_button_press(buttons_status))
  349.       {
  350.          case 1:
  351.             tempn=stnumber-power_ten[pos];
  352.             if(tempn<10000){ stnumber = tempn; };
  353.             break;
  354.          case 2:
  355.             tempn=stnumber+power_ten[pos];
  356.             if(tempn<10000){ stnumber = tempn; };
  357.             break;
  358.          case 4:
  359.             pos=(pos+3) % 4;
  360.             break;
  361.          case 8:
  362.             returnflag=1;
  363.             break;
  364.          default:
  365.             break;
  366.       }
  367.       load_num(stnumber,one_bit_shift[pos],codpointer);
  368.    }
  369.    return stnumber;
  370. }
  371.  
  372. void write_play()
  373. {
  374.    int play[] = { 0x3B, 0x77, 0x0E, 0x67 };
  375.    output_low(oe_in);
  376.    while(true)
  377.    {
  378.       update_disp(&play);
  379.    }
  380. }
  381.  
  382. // ########################### END OF DISPLAY FUNCTIONS ###########################
  383.  
  384. // Count: 0~64 (8 "macro" steps * 8 microsteps)
  385. // Steps: 1 (full), 2 (half), 4 (1/4 micro), 8 (1/8 micro). Needs more steps?
  386. // Even when not using 1/8 microstepping, the software will "ignore" smaller increases till it
  387. // reaches next step. At least that is my plan.
  388. // #### This piece of code will be slight more ad hoc, as it is designed to work on an specific
  389. // circuitry. Though the code might be useful for something...
  390.  
  391. const int hdrive_map[] = { 1, 4, 4, 2, 2, 8, 8, 1 }; // Odd: decrease, Even: increase PWM
  392. const int htrisa_map[] = { 4, 1, 2, 4, 8, 2, 1, 8 };
  393. const int16 pwm_ustep[] = { 179, 331, 463, 580, 686, 781, 868, 949 }; // PWM values calculated for
  394. // timer2 without prescale (divide by 1) and PR2 = 150.
  395.  
  396. // 341, 482, 591, 682, 762, 835, 902, 964 -> sqrt
  397. // 179, 331, 463, 580, 686, 781, 868, 949 -> log
  398. // 143, 271, 387, 497, 602, 705, 808, 913 -> log(tan)
  399. //  90, 180, 274, 372, 477, 591, 716, 858 -> tan
  400. // 114, 227, 341, 455, 568, 682, 796, 909 -> lin
  401.  
  402. void motor_ustep_pwm(int steps, int count)
  403. {
  404.    int macrostep = count / 8;
  405.    int ustep = count % 8;
  406.  
  407.    if(steps>=4)
  408.    {
  409.       output_a(hdrive_map[macrostep]);
  410.       set_tris_a(htrisa_map[macrostep]);
  411.       ustep = (macrostep & 0x01)? 7-ustep : ustep;
  412.       ustep = steps==4 ? ustep&0x0E : ustep;
  413.       set_pwm1_duty(pwm_ustep[ustep]);
  414.    }
  415. }
  416.  
  417. /*void motor_ustep_pwm(int steps, int count) // Old version, didn't mananged to get it right.
  418. {
  419.    int pintmp;
  420.    int1 flag;
  421.    int hstep=count/8; // Which "macrostep" (halfstep) it should be at.
  422.    
  423.    int next=hdrive_map[hstep]; // Next pin map to set
  424.    
  425.    int curr = hstep;
  426.    curr = (curr+7) % 0x08; // Desceases 1 with rollover.
  427.    curr=hdrive_map[hstep]; // Old pin map.
  428.    
  429.    if(next>curr) // Detects if it needs to rise or turn down pin.
  430.    {
  431.       pintmp=next-curr;
  432.       flag=1;
  433.    }
  434.    else
  435.    {
  436.       pintmp=curr-next;
  437.       flag=0;
  438.    }
  439.    
  440.    set_tris_a(pintmp); // Sets the pin to increase/decrease to high impedance, allowing the PWM
  441.    // signal to pass though.
  442.    
  443.    output_a(next); // Sets the pin map. As the current changing pin it's floating, doesn't
  444.    // matters if it is set up or down.
  445.    
  446.    int ustep=count % 0x08; // Which microstep it should be at. 3 LSBs of count.
  447.    
  448.    ustep = flag ? ustep : 7-ustep; // If it needs to decrease the PWM value of the pin (turning it
  449.    // down), it "mirrors" the ustep.
  450.    
  451.    set_pwm1_duty(pwm_usteps[ustep]); // Sets the duty to the PWM CCP. pwm_ustep rises (0%-100%).
  452. }*/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement