Advertisement
Guest User

Gadorach_PICFIX_LCD12F1572

a guest
Apr 16th, 2016
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.36 KB | None | 0 0
  1. //PIC 12F1572 Shift LCD Example (MPLABX)
  2. //By Gadorach
  3.  
  4. //PIC 12F1572 Pins
  5. //Pin 1 = +5V
  6. //Pin 8 = 0V (GND)
  7. //Pin 4 = LCD Enable
  8. //Pin 6 = 74HC595 Clock OUT
  9. //Pin 7 = 74HC595 Data OUT
  10.  
  11. //74HC595 Pins
  12. //Pin 11 + Pin 12 = 74HC595 Clock IN (tie Pins 11&12 as a clock IN source)
  13. //Pin 14 = 74HC595 Data IN
  14. //Pin 15 = LCD D4 OUT
  15. //Pin 1  = LCD D5 OUT
  16. //Pin 2  = LCD D6 OUT
  17. //Pin 3  = LCD D7 OUT
  18. //Pin 4  = LCD RS OUT
  19.  
  20. // CONFIG1
  21. #pragma config FOSC = INTOSC    // (INTOSC oscillator; I/O function on CLKIN pin)
  22. #pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
  23. #pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
  24. #pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
  25. #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
  26. #pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
  27. #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
  28.  
  29. // CONFIG2
  30. #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
  31. #pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
  32. #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
  33. #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
  34. #pragma config LPBOREN = OFF    // Low Power Brown-out Reset enable bit (LPBOR is disabled)
  35. #pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)
  36.  
  37. // INCLUDES
  38. #include <xc.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include <pic12f1572.h>         //This is the PIC's own setting file, change for your PIC if it's not a 12F1572
  42.  
  43. // SETUP CLOCK
  44. #define _XTAL_FREQ = 16000000; //define 16MHz internal frequency for software use (dosen't set it, just defines it in Hz)
  45.  
  46. // DEFINITIONS
  47. #define LCD_ENABLE LATAbits.LATA2 //LCD enable line for HD44780 parallel LCD
  48. #define LCD_DATA LATAbits.LATA5 //LCD data line for 74HC595
  49. #define LCD_CLK LATAbits.LATA4
  50. #define ON 1                    //define bool values for readability
  51. #define OFF 0
  52.  
  53. // GLOBAL VARIABLES
  54.  
  55. // FUNCTIONS
  56. void setup_clock()
  57. {
  58.     //configure Timer0
  59.     OSCCON = 0x7A;              //Setup internal osc to 16MHz
  60.     OSCSTAT = 0x00;             //disable unnecessary modules
  61.     OSCTUNE = 0x00;             //set INTOSC to factory calibrated settings
  62.    
  63.     OPTION_REGbits.T0CS = 0;    //enable timer mode on timer 0
  64.     OPTION_REGbits.PSA = 0;     //assign prescaler to timer0
  65.     OPTION_REGbits.PS = 0b111;  //set prescaler to 256 (64us per increment)
  66. }
  67.  
  68. void setup_ports()
  69. {
  70.     TRISAbits.TRISA2 = 0;       // Set Pins (4-RA2, 6-RA4, 7-RA5) to Output
  71.     TRISAbits.TRISA4 = 0;
  72.     TRISAbits.TRISA5 = 0;
  73.     ANSELAbits.ANSA2 = 0;       // Set Pins (4-RA2, 6-RA4) to digital I/O
  74.     ANSELAbits.ANSA4 = 0;       // 7-RA5 has no ADC, so it's already digital
  75. }
  76.  
  77. void delay_ms(unsigned long time)                               //delay function based on OSCCON settings and T0, creates delays in ms
  78. {
  79.     if (time <= 16)                                             //only likes it if it's set to less than 16ms
  80.     {
  81.         TMR0 = 0;                                               //reset timer0
  82.         unsigned long timeHold = (((time * 1000) / 64) + 0.5);  //turn ms into us and round to nearest int after dividing by the base of 64us
  83.         while (TMR0 < timeHold);                                //input time divided by 64us = delay
  84.     }
  85.     else                                                        //when it's greater than 16ms
  86.     {
  87.         for(int i=0; i<time; i++)                               //loop to reach larger values of ms
  88.         {
  89.             TMR0 = 0;                                           //reset timer0
  90.             while (TMR0 < (1024/64));                           //only likes clean multiples for some reason
  91.         }
  92.     }
  93. }
  94.  
  95. void delay_50ms() //generalize delays, for tuning
  96. {
  97.     delay_ms(50);
  98. }
  99.  
  100. int string_length(char *instr)  //find the size of a character array
  101. {
  102.    char length = 0;              //setup the length variable so we can calculate the array size
  103.    while(*(instr + length)){    //using a pointer to check the contents of the array, increment the array position with the length variable
  104.                                 //an alternate way to do this would be "while (instr[length] > 0)", or even just "while (instr[length])". both have the same result,
  105.                                 //but pointers are preferable due to convention and operation speed. Basically, it's more professional code.
  106.       length++;                 //as long as the value of the position is greater than null(0x0, \0, 0b0, whatever representation you like), the loop continues and increments the length counter
  107.    }                            //amusingly, '0', the character for zero, is not equal to the null byte, as the character is actually stored in ASCII, and the true value is 0x30, or 0b00011110, where as null is 0x0, or 0b00000000
  108.    return length;               //return the final length of the array, which is found thanks to all arrays ending with a null byte
  109. }
  110.  
  111. void write_nibble(char RS, unsigned short nibble) //RS is the command state. 1 = data, 0 = command. nibble is the character to be sent to the display, converted to it's binary representation
  112. {                                           // begin by writing the RS bit
  113.     LCD_ENABLE = OFF;                       // disable the LCD's input command, so we can setup the input data
  114.     LCD_CLK = 0;                            // setup the clock's first state for the shift register
  115.     LCD_DATA = RS;                          // make the first bit equal our RS state, so the LCD knows the input is either a command or regular character data
  116.     LCD_CLK = 1;                            // start the clock pulse for the shift register
  117.     LCD_CLK = 0;                            // complete the high to low trigger pulse for the shift register's clock
  118.                                             // shift in our input nibble's 4 bits
  119.     unsigned short mask = 0b1000;           // start by setting up the bit position to be checked. in this case, we're starting with position 4 from the LSB (0b1000, equal to a decimal 8)
  120.     for (unsigned short i=0; i<4; i++){     // we're going to write our data from MSB to LSB (0b1000 to 0b0001, for visual example) to match the shift register's outputs to the LCD's inputs
  121.         unsigned short flag = nibble & mask;// now we do a bitwise AND (if both the mask bit and the input bit are equal, we get a 1 for that position and a 0 for all others. else, we get a completely 0 nibble returned. So, if our nibble was 0b1011, and our mask was 0b1000, we would get a 0b1000 as the output. However, if our mask was instead 0b0100, we would get a 0b0000)
  122.         if(flag == 0){                      // if the flag returned as a 0b0000 (mask and nibble position not equal)
  123.             LCD_DATA = 0;                   // data at that position was a 0, so write a 0 to the shift register
  124.         }
  125.         else{                               // if it was anything else (0b1000, 0b0100, 0b0010, or 0b0001)
  126.             LCD_DATA = 1;                   // we know it was a 1 at that position, so we write a 1 to the shift register instead
  127.         }                                   // we do it this way because it's super fast and uses almost no memory, as well as being supported by every traditional microprocessor in existence
  128.         LCD_CLK = 1;                        // start the shift register's clock
  129.         LCD_CLK = 0;                        // and finish it with the high to low trigger of the shift register's clock inputs
  130.         mask = mask >> 1;                   // shift the mask right by one bit position (0b1000 to 0b0100, for example)
  131.     }                                       // repeat until all four bit positions of the nibble are handled
  132.                                             // one more clock to catch up because our shift register clocks are tied with one behind the other, so our data won't appear at the output until one clock after we set it.
  133.                                             // a good example would be that the memory of the register holds 0b11010000, but it's outputs show 0b10100000. this is because they are one clock cycle behind each other normally.
  134.                                             // by clocking it one extra time, we can make them equal, yet not. the data we want ends up at the output, but the memory of the register shows an additional bit of data.
  135.     LCD_CLK = 1;                            // so, we'd have 0b11101000 in the memory now, but 0b11010000 at the output, which is what we wanted all along.
  136.     LCD_CLK = 0;                            // clock the shift register (we don't care about the next bit's value, as it won't appear at the output until it doesn't matter)
  137.     LCD_DATA = 0;                           // reset the data line, so we aren't using more current than necessary until we need to pass data again
  138.     LCD_ENABLE = ON;                        // enable the LCD controller's input so it can read the inputted bits from the shift register's output pins
  139.     LCD_ENABLE = OFF;                       // disable it again so we can change the data on the shift register without affecting the LCD's state
  140. }
  141.  
  142. void write_CMD(unsigned short command_bits) // used to more easily pass our command data to the LCD writer function. we need to chop our command in half though, as we need to pass 8 bits, but we're operating in 4-bit mode.
  143. {
  144.     unsigned short upper_nibble = (command_bits / 0b00010000);  // remove the lower half of the byte, placing the upper half in the lower part of the nibble. this is achieved by dividing the input by 0b10000, as the result is equivalent to the upper 4 bits, which are now in the lower 4 bits thanks to math, as any remainder is chopped off in the conversion.
  145.     unsigned short lower_nibble = (command_bits & 0b00001111);  // remove the upper 4 bits of the byte. put simply, we just do a bitwise AND again, with 0b00001111 as the mask. the resulting flag is whatever the lower nibble amounts to. honestly, this is un-necessary, but whatever, we'll do it for consistency's sake
  146.     write_nibble(0, upper_nibble);                              // pass the upper nibble
  147.     write_nibble(0, lower_nibble);                              // pass the lower nibble
  148. }
  149.  
  150. void write_DATA(unsigned short data_bits) // used to more easily pass our character data to the LCD writer function
  151. {
  152.     unsigned short upper_nibble = (data_bits / 0b00010000); // same as the command sequence
  153.     unsigned short lower_nibble = (data_bits & 0b00001111);
  154.     write_nibble(1, upper_nibble);                          // same as before, the only difference is we're sending character data, so we send a 1 instead of a 0
  155.     write_nibble(1, lower_nibble);
  156. }
  157.  
  158. void write_TEXT(char line, char position, char *StrData)    //this is a joined function that writes the starting position, and the character data, all in one go
  159. {
  160.     unsigned char length = string_length(StrData);          // get the length of the input string, for multiple handling sections of this code
  161.    
  162.     if (position == 0){                                     // this is a special case, where if the position is entered as '0', the characters are automatically centered
  163.         unsigned char center = (((16 - length)/2)+1);       // by declaring a new variable, the code will automatically chop off any decimals, which prevents errors.
  164.                                                             // we take the LCD line length, subtract the length of the character array, divide that result by 2, and add 1 to correct for the first position
  165.         position = center;                                  // now we just make the start position equal to the calculated centered starting position
  166.     }
  167.     unsigned short temp = 127 + position;                   // LCD's have the ability to scroll data across their screen, so they have a really big buffer for characters, which just means that there are more characters slots than there are visible characters on the display.
  168.                                                             // so, the actual starting position for visible characters is position 128, and it ends at position 133.
  169.    
  170.     if (line == 2){                                         // in order to display text on line 2 of the LCD, we need to move to a further position on the LCD's buffer
  171.         temp += 64;                                         // specifically, position 1 of line two is at the buffer position of 128+64, which is position 192
  172.     }
  173.     write_CMD(temp);                                        // when we write commands now, as the LCD has already been initialized and set to our mode, commands are recognized as a position request. by writing the new position as a command, we can jump to it immediately instead of cycling through the buffer until we get back to where we want to write our characters
  174.     delay_50ms();                                           // give the LCD some time to jump to the set position
  175.    
  176.     for (char i = 0; i < length; i++){                      // we have to write the character array one character at a time, so we loop through it
  177.         temp = StrData[i];                                  // this is pretty basic, just write whatever character that's found in the character array at the loop's incremented position to the LCD, until you reach the end of the loop
  178.         write_DATA(temp);
  179.     }
  180. }
  181.  
  182. void initialize_LCD()   //here, we setup the LCD using 4bit mode
  183. {
  184.     delay_50ms();       // wait until the LCD finishes it's power-up sequence, in case our microcontroller beats it to the punch
  185.     write_CMD(0x20);    // wake up the LCD
  186.     delay_50ms();       // give the LCD time to finish the last instruction
  187.     write_CMD(0x20);
  188.     delay_50ms();
  189.     write_CMD(0x20);    // it takes three requests to wake it up. this is to prevent random noise from waking it up
  190.     delay_50ms();
  191.     write_CMD(0x28);    // set the LCD to 4-bit mode, 2 LCD lines, 5x7 font size
  192.     delay_50ms();
  193.     write_CMD(0x0C);    // turn the display on, and disable the cursor
  194.     delay_50ms();
  195.     write_CMD(0x06);    // set the LCD to character entry mode, and make it automatically move to the next position after the last entry. Also disable display scrolling.
  196.     delay_50ms();
  197.     write_CMD(0x01);    // clear the LCD
  198.     delay_50ms();
  199. }
  200.  
  201. // MAIN LOOP
  202.  
  203. void main(void)
  204. {
  205.     char Message1[] = "PIC12F1572 LCD";     //text to display
  206.     char Message2[] = "with a 74HC595";
  207.    
  208.     setup_clock();                      // setup the internal clock module
  209.     setup_ports();                      // setup the I/O ports
  210.     initialize_LCD();                   // startup the LCD
  211.    
  212.     while(1){                           // do forever
  213.         write_TEXT(1, 0, Message1);     // write a character array to the LCD
  214.         write_TEXT(2, 0, Message2);     // write a character array to the LCD
  215.         delay_ms(1500);                 // put the microcontroller to sleep for a bit
  216.         //write_CMD(0x01);              // clear the LCD (uncomment to implement)
  217.         //delay_ms(1000);
  218.     }
  219.     return;                             //End program (never reached)
  220. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement