Controlling a HD44780 LCD on a BeagleBone (Part 1)
Posted: March 22, 2012 | Author: Nuno Alves | Filed under: Embedded Systems, Programming Languages | Tags: Beagle Bone, C/C++, Doxygen | 8 Comments »Hitachi HD44780 compatible LCD screens are those common displays we see all over the place. Getting them to display some information is fairly easy, as all you have to do is send the appropriate bits to each of the LCD screen inputs.
In this post I will describe the steps I took to integrate a Hitachi HD44780 LCD with the BeagleBone board. First of all you need a LCD display. From my Arduino days, I had a couple of them laying around my lab. In this tutorial, I used a fairly inexpensive Microtivity LCD Module 1602. Because this LCD module requires a 5V source, and the BeagleBone GPIO pins only provide 3.3V, you need to get power from other sources: either connect a 5V DC adaptor to the BeagleBone and use P9_5 which provides +5V, or use any adapter that you have in your house to deliver the appropriate voltage with a voltage regulator. If you opt for the voltage regulator route, I recommend you get a 9V adaptor and use this neat sparkfun kit, just make sure the ground of this external voltage supply is connected to the ground pin of the BeagleBone. Of course you can bypass all these problems, by getting a +3.3V LCD display, but they will be more expensive.
Once you have acquired the LCD and connected to an appropriate power source, use the following connections.
| LCD Pin # | LCD Pin Description | LCD Pin Connects to |
| 1 | VSS | Ground |
| 2 | VDD | +5V |
| 3 | VO | Attach a potentiometer here to adjust contrast |
| 4 | Register Select (RS) | BeagleBone pin P8_4 |
| 5 | Read/Write (R/W) | Ground |
| 6 | Clock Enable (E) | BeagleBone pin P8_3 |
| 7 | Data Bit 0 | Unconnected |
| 8 | Data Bit 1 | Unconnected |
| 9 | Data Bit 2 | Unconnected |
| 10 | Data Bit 3 | Unconnected |
| 11 | Data Bit 4 | BeagleBone pin P8_5 |
| 12 | Data Bit 5 | BeagleBone pin P8_11 |
| 13 | Data Bit 6 | BeagleBone pin P8_12 |
| 14 | Data Bit 7 | BeagleBone pin P8_14 |
| 15 | Backlight Anode (+) | +5V (or lower) |
| 16 | Backlight Cathode (-) | Ground |
Source-Code
The code below will prompt you to enter a bitstream into the LCD screen. Keep in mind that you need the library beagle_gpio.h, or else the program will not compile. You can find all the necessary files to run the program in the following github site (https://github.com/nunoalves/BeagleBone-HD44780). You can compile, and run, this code by typing the following commands in the BeagleBone terminal:
gcc beagle_gpio.o example_01.c
./a.out
rm -f a.out
Once the code is compiled you will be asked to type the appropriate bitstream. If you type the following bits, you will type the word “hi” on the LCD.
If you would like to manually type different characters look at the following table:
… And here is a screen shot of the program output.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <time.h> #include "beagle_gpio.h" #define DISPLAY_DATA_ON_SCREEN 1 /** * @brief This program (example_01.c) shows how to write data into a * HD44780 LCD display using the manual process of setting up every * single pin with a binary value. For a complete description of these * (and more) HD44780 commands, read the article by Julyan llett, * "How to use intelligent L.C.D.s - Part One". * * @section Setup * Set-up the HD44780 screen as follows: * Pin 1 - VSS - Connect to ground * Pin 2 - VCC - Connect to +5V (or lower voltage depending on your LCD) * Pin 3 - VO - Attach a potentiometer here to adjust contrast * Pin 4 - Register Select (RS) - Connect to P8_4 * Pin 5 - Read/Write (R/W) - Connect to ground * Pin 6 - Clock (Enable) - Connect to P8_3 * Pin 7 - Data Bit 0 - Do not connect anything to it * Pin 8 - Data Bit 1 - Do not connect anything to it * Pin 9 - Data Bit 2 - Do not connect anything to it * Pin 10 - Data Bit 3 - Do not connect anything to it * Pin 11 - Data Bit 4 - Connect to P8_5 * Pin 12 - Data Bit 5 - Connect to P8_11 * Pin 13 - Data Bit 6 - Connect to P8_12 * Pin 14 - Data Bit 7 - Connect to P8_14 * Pin 15 - Backlight Anode (+) - Connect to +5V (or lower) * Pin 16 - Backlight Cathode (-) - Connect to ground * * @section Compilation * Compile, and run, this code on the beagleBone terminal with the command: * * gcc -c beagle_gpio.c ; gcc beagle_gpio.o example_01.c ; ./a.out ; rm -f a.out ; * * @return Returns a 1 upon succesful program termination **/ int main() { //Specifies which pins we are going to use int selectedPins[]={P8_14,P8_12,P8_11,P8_5,P8_4,P8_3}; //A little description for each of the selectedPins[]. //This makes debugging a LOT easier... const char *pinDescription[] = {"DB7","DB6","DB5","DB4","RS","E"}; int nbr_selectedPins=sizeof(selectedPins)/sizeof(*selectedPins); unsigned int data_to_write=0; //these two lines initalize all the selected pins struct gpioID enabled_gpio[nbr_selectedPins]; initialize_each_enabled_gpio(enabled_gpio,selectedPins,nbr_selectedPins); //display some information on the beglebone screen if (DISPLAY_DATA_ON_SCREEN) display_each_enabled_gpio(enabled_gpio,nbr_selectedPins,pinDescription); //======================================================================== //START: Manually feeding commands into the HD44780 LCD //======================================================================== //For example: the following command will type the letter 'hi' in the //LCD //RS ; DB7 ; DB6 ; DB5 ; DB4 ; Stop sending commands? //0 ; 0 ; 0 ; 1 ; 1 ; No - (this command initializes the screen) //0 ; 0 ; 0 ; 1 ; 0 ; No - (this command send LCD to 4 bit mode) //0 ; 0 ; 0 ; 0 ; 0 ; No - (enable display with cursor part 1) //0 ; 1 ; 1 ; 1 ; 1 ; No - (enable display with cursor part 2) //1 ; 0 ; 1 ; 1 ; 0 ; No - (first part of character 'h') //1 ; 1 ; 0 ; 0 ; 0 ; No - (second part of character 'h') //1 ; 0 ; 1 ; 1 ; 0 ; No - (first part of character 'i') //1 ; 1 ; 0 ; 0 ; 1 ; No - (second part of character 'i') //0 ; 0 ; 0 ; 1 ; 1 ; Yes - (go back into 8 bit mode) int pinSelect, terminate_operation, i, pinData; terminate_operation=0; while (terminate_operation==0) { data_to_write=0; printf("Write value for pin %s:\n",pinDescription[4]); scanf("%d",&pinData); data_to_write=bitWrite(data_to_write,pinData,i); turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins,1,pinDescription); for (i=0; i<4; i++) { printf("Write value for pin %s:\n",pinDescription[i]); scanf("%d",&pinData); data_to_write=bitWrite(data_to_write,pinData,i); turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins,1,pinDescription); } pulsePin(enabled_gpio,data_to_write,nbr_selectedPins,DISPLAY_DATA_ON_SCREEN, pinDescription, 5, 1); printf("Stop sending commands? (1=yes):\n"); scanf("%d",&terminate_operation); } //======================================================================== //END: Manually feeding commands into the HD44780 LCD //======================================================================== //we should now tell the OS that we are done with the GPIOs cleanup_GPIO(enabled_gpio,selectedPins,nbr_selectedPins); } |
Finally, if you would like to know more about controlling these LCD devices I suggest this website, and the article “How to use intelligent L.C.D.s – Part One” by Julyan llett, which can be found here.
In a future post, I will release another library that will allow us to automatically display text without manually typing the entire bitstream.
Addressing multiple BeagleBone GPIO pins in C
Posted: March 17, 2012 | Author: Nuno Alves | Filed under: Embedded Systems, Programming Languages | Tags: Beagle Bone, C/C++, Doxygen | 2 Comments »Since I am in the process of writing a HD44780 compatible LCD display driver for the beagle bone, I need to have a very simple piece of C code that writes data to multiple GPIO pins at the same time. In particular I need to have a function that when given a binary value, will turn ON/OFF may GPIO pins. For example, lets say the end-user is using the following 5 GPIO pins, P8_3, P8_4, P8_5, P8_11 and P8_12. Now lets say he just wants to turn ON pins P8_4 and P8_11, while leaving the others OFF. I created a neat piece of code that takes the pin selection in a binary format, and performs the appropriate ON/OFF switching operations. The latest version of this code can be found at the following github repository, https://github.com/nunoalves/BeagleBone-GPIO-in-C.
While this code works quite well, it is not really complete. For starters, I haven’t included all BeagleBone GPIO pins. You can include them all yourself in the function initialize_each_enabled_gpio(). Please refer to this earlier blog post for hints about how to do it. I also haven’t split this code into a different header file, so it’s its own library. I will do so, after I release the drivers for the HD44780 LCD display. Finally, this code could need some cleaning and optimization…
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
/** * @file main.c * * @brief Addressing multiple BeagleBone GPIO pins in C * @details Self-contined program that will turn ON/OFF a specified group of * of GPIO pins in the beaglebone board. This code was possible due to prior * efforts by Christian Mitchell, Markus Klink and Matt Richardson. To compile * and run this code, type something like "gcc main.c ; ./a.out ; rm -f a.out" * * @author Nuno Alves * @date 17/March/2012 **/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <assert.h> #include <time.h> //If DEBUG is defined to TRUE (1), intermediate debug messages will be displayed //when code is executing. You may set it to FALSE (0) for no debug information. #define DEBUG 1 //The following #defines make it easier for the user to add differnt pins #define P8_3 0 #define P8_4 1 #define P8_5 2 #define P8_11 3 #define P8_12 4 /** @brief Contains all elements which we need to pass into the beagleboard * to specify which GPIO pin we want to turn ON/OFF **/ struct gpioID { char *PINNAME; //eg. P8_3 char * GPIOID; //e.g: gpio1[6] int GPIONUMBER; //e.g: 38 char *GPIOMUX; //e.g: gpmc_ad6; }; /** * @brief Writes a value to a particular GPIO, logic-HIGH or logic-LOW. * @param GPIONUMBER The GPIO number (e.g. 38 for gpio1[6]) * @param value The logic value we want to write to the GPIO (0 or 1) **/ void write_GPIO_value(int GPIONUMBER, int value) { char export_filename[50]; FILE *f = NULL; sprintf(export_filename, "/sys/class/gpio/gpio%d/value",GPIONUMBER); f = fopen(export_filename,"w"); assert(f!=NULL); fprintf(f , "%d",value); pclose(f); } /** * @brief Tells the OS that we are done with using GPIOs * @param selected_GPIOs[] initialized array of gpioID. * @param selectedPins[] Array with the user specified pins * @param sizeof_selectedPins Number of pins that were specified by the user **/ void cleanup_GPIO(struct gpioID selected_GPIOs[],int selectedPins[], int nbr_selectedPins) { char export_filename[50]; FILE *f = NULL; int i; for (i=0; i<nbr_selectedPins;i++) { //unexport the pin f = fopen("/sys/class/gpio/unexport","w"); assert(f!=NULL); fprintf(f, "%d",selected_GPIOs[i].GPIONUMBER); pclose(f); } } /** * @brief Initializes all the user specified pins * @param selected_GPIOs[] Un-initialized array of gpioID. At the end of this * function, the selected_GPIOs[] array will be initialized. * @param selectedPins[] Array with the user specified pins * @param sizeof_selectedPins Number of pins that were specified by the user **/ void initialize_each_enabled_gpio(struct gpioID selected_GPIOs[],int selectedPins[], int nbr_selectedPins) { int i; //this variable will contain the location of the file that we need to //set to mode 7 char export_filename[50]; FILE *f = NULL; //You are not allowed to use more than 32 active pins. This is not a //beagleboard limitation, but a limitation of this code. assert (nbr_selectedPins<=32); //cycles every userdefined pin, and inializes its contents in the //selected_GPIOs[] array. for (i=0; i<nbr_selectedPins;i++) { switch (selectedPins[i]) { case P8_3: selected_GPIOs[i].PINNAME="P8_3"; selected_GPIOs[i].GPIOID="gpio1[6]"; selected_GPIOs[i].GPIONUMBER=38; selected_GPIOs[i].GPIOMUX="gpmc_ad6"; break; case P8_4: selected_GPIOs[i].PINNAME="P8_4"; selected_GPIOs[i].GPIOID="gpio1[7]"; selected_GPIOs[i].GPIONUMBER=39; selected_GPIOs[i].GPIOMUX="gpmc_ad7"; break; case P8_5: selected_GPIOs[i].PINNAME="P8_5"; selected_GPIOs[i].GPIOID="gpio1[2]"; selected_GPIOs[i].GPIONUMBER=34; selected_GPIOs[i].GPIOMUX="gpmc_ad2"; break; case P8_11: selected_GPIOs[i].PINNAME="P8_11"; selected_GPIOs[i].GPIOID="gpio1[13]"; selected_GPIOs[i].GPIONUMBER=45; selected_GPIOs[i].GPIOMUX="gpmc_ad13"; break; case P8_12: selected_GPIOs[i].PINNAME="P8_12"; selected_GPIOs[i].GPIOID="gpio1[12]"; selected_GPIOs[i].GPIONUMBER=44; selected_GPIOs[i].GPIOMUX="gpmc_ad12"; break; default: //This statement should never be reached... since //the program won't even compile if the user //specified a bad pin specification. break; } } //set the each mux pin to mode 7 for (i=0; i<nbr_selectedPins;i++) { //set mux to mode 7 sprintf(export_filename, "/sys/kernel/debug/omap_mux/%s", selected_GPIOs[i].GPIOMUX); f = fopen(export_filename,"w"); if (f == NULL) { printf( "\nERROR: There was a problem opening /sys/kernel/debug/omap_mux/%s\n", selected_GPIOs[i].GPIOMUX); printf("\n%s\t%s\t%s\t%d\n\n", selected_GPIOs[i].PINNAME, selected_GPIOs[i].GPIOID, selected_GPIOs[i].GPIOMUX, selected_GPIOs[i].GPIONUMBER); assert(f!=NULL); } fprintf(f, "7"); pclose(f); //export the pin f = fopen("/sys/class/gpio/export","w"); if (f == NULL) { printf( "\nERROR: There was a problem opening /sys/kernel/debug/omap_mux/%s\n", selected_GPIOs[i].GPIOMUX); printf("\n%s\t%s\t%s\t%d\n\n", selected_GPIOs[i].PINNAME,selected_GPIOs[i].GPIOID,selected_GPIOs[i].GPIOMUX,selected_GPIOs[i].GPIONUMBER); assert(f!=NULL); } fprintf(f, "%d",selected_GPIOs[i].GPIONUMBER); pclose(f); //set the appropriate io direction (out) sprintf(export_filename, "/sys/class/gpio/gpio%d/direction", selected_GPIOs[i].GPIONUMBER); f = fopen(export_filename,"w"); if (f == NULL) { printf( "\nERROR: There was a problem opening /sys/class/gpio/gpio%d/direction\n", selected_GPIOs[i].GPIONUMBER); printf("\n%s\t%s\t%s\t%d\n\n",selected_GPIOs[i].PINNAME,selected_GPIOs[i].GPIOID,selected_GPIOs[i].GPIOMUX,selected_GPIOs[i].GPIONUMBER); assert(f!=NULL); } fprintf(f , "out" ); pclose(f); write_GPIO_value(selected_GPIOs[i].GPIONUMBER,0); } } /** * @brief Displays all information for each of the user specified pins. * This function has no purpose besides live debugging. * @param selected_GPIOs[] An valid (initialized) array of gpioID * @param nbr_selectedPins Number of pins that were specified by the user **/ void display_each_enabled_gpio(struct gpioID selected_GPIOs[],int nbr_selectedPins) { int i; printf("\n"); printf("PINNAME\tGPIOID\t\tGPIOMUX\t\tGPIONBR\n"); printf("++++++++++++++++++++++++++++++++++++++++++++++++\n"); for (i=0; i< nbr_selectedPins; i++) { printf("%s\t%s\t%s\t%d\n",selected_GPIOs[i].PINNAME,selected_GPIOs[i].GPIOID,selected_GPIOs[i].GPIOMUX,selected_GPIOs[i].GPIONUMBER); } printf("\n"); } /** * @brief Cycles through the entire array of selected_GPIOs[] and turns ON/OFF * each associated GPIO. * @param selected_GPIOs[] An valid (initialized) array of gpioID * @param data_to_write An unsigned integer (32 bits), where each bit contains the * information regarding the status of each pin: 1=turn pin ON, 0=turn pin OFF * @param nbr_selectedPins Number of pins that were specified by the user **/ void turn_ON_OFF_pins(struct gpioID selected_GPIOs[],unsigned int data_to_write,int nbr_selectedPins) { int i; for (i=0;i<nbr_selectedPins;i++) { //code that turns ON/OFF a pin write_GPIO_value(selected_GPIOs[i].GPIONUMBER,bitRead(data_to_write,i)); //this is just for debugging purposes if (DEBUG) { if (bitRead(data_to_write,i) == 1) printf("turning ON"); else printf("turning OFF"); printf( "\t%s (%s %d %s)\n",selected_GPIOs[i].PINNAME,selected_GPIOs[i].GPIOID,selected_GPIOs[i].GPIONUMBER,selected_GPIOs[i].GPIOMUX); } } if (DEBUG) printf("\n"); } /** * @brief Writes a bit into a particular location in an unsigned int variable (32 bits) * @param groupOfBits Some data resides in these 32 bits * @param bitVal The bitVal is wither a 1 or a 0 * @param bitLoc Where in the groupOfBits, the value of bitVal will be written. * Keep in mind that the bitLoc values ranges 0 to 31 * @return Returns the updated groupOfBits **/ unsigned int bitWrite(unsigned int groupOfBits, int bitVal, int bitLoc) { if (bitVal==1) groupOfBits |= 1 << bitLoc; else groupOfBits &= ~(1 << bitLoc); return(groupOfBits); } /** * @brief Returns the bit value of a particular location in an unsigned int variable (32 bits) * @param groupOfBits Some data resides in these 32 bits * @param bitLoc Where in the groupOfBits, the value of bitVal will be written * @return Retuns an integer with the bit value (1 or 0) that resides in bitLoc **/ int bitRead(unsigned int groupOfBits, int bitLoc) { unsigned int bit = groupOfBits & (1 << bitLoc); if (bit!=0) bit=1; return(bit); } /** * @brief This is the main function with a sample test program. * @return Returns a 1 upon succesful program termination **/ int main() { int selectedPins[]={P8_3,P8_4,P8_5,P8_11,P8_12}; int nbr_selectedPins=sizeof(selectedPins)/sizeof(*selectedPins); //This variable data_to_write will contain the information which specified //the pins that will be ON or OFF unsigned int data_to_write=0; //Define an array of gpios which will contain all the user selected pins and //its associated information struct gpioID enabled_gpio[nbr_selectedPins]; //Initializes all user defined pins initialize_each_enabled_gpio(enabled_gpio,selectedPins,nbr_selectedPins); if (DEBUG) display_each_enabled_gpio(enabled_gpio,nbr_selectedPins); //At this point in the code there are 5 selected pins: //P8_3, P8_4, P8_5, P8_11, P8_12 //All these pins are intially OFF. //If I just want to turn ON pins P8_4 and P8_11, I need to look at my //selectedPins[] array: Since positions start at 0, pin P8_4 is on position 1 //and P8_11 is on position 3. Therefore I need to set the bits 1 and 3 on //the variable data_to_write. In base 2, setting bit1 and 3 looks like: //00000000 00000000 00000000 00001010 (base 2) //Which is equivalent to the value 10, in base 10. data_to_write=10; turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins); sleep(1); //sleep for 1 second //In addition to the pins that are already ON (P8_4, P8_11), I now //also want to turn ON pin P8_12. Since P8_12 is on position 4, I //need to write a 1 to position 4. data_to_write=bitWrite(data_to_write,1,4); turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins); //Of course, I could have done something like //data_to_write=26; //turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins); //Which would have yielded the exact same outcome. sleep(1); //sleep for 1 second //Now I would like to turn OFF pin P8_11, while leaving pins P8_4 & P8_12 ON //Since pin P8_11 is on position 3 data_to_write=bitWrite(data_to_write,0,3); turn_ON_OFF_pins(enabled_gpio,data_to_write,nbr_selectedPins); sleep(1); //sleep for 1 second //we should now tell the OS that we are done with the GPIOs cleanup_GPIO(enabled_gpio,selectedPins,nbr_selectedPins); } |
Here is a screenshot of the program running in the BeagleBone.

To compile and run this code just type the following commands in the BeagleBone terminal.
./a.out
Finally, for those of you that are Doxygen fans, here is a printout of the code documentation.





