//============================================================================ // This is an example use of the Crystalfontz CFA634 I2C 20x4 LCD, driven by // an Arduino Uno or Seeeduino v4.2 set to 5v. // // The demonstration shows menus (stored in flash / PROGMEM) and bar graphs. // // Wiring information is available in our forum: // // https://forum.crystalfontz.com/showthread.php/7483 // // There is a video of the operation in action on YouTube: // // https://www.youtube.com/watch?v=lsuUchaF-FM // // 2018 - 11 - 01 Brent A. Crosby / Crystalfontz // // Display is Crystalfontz CFA634-TFH-KC // https://www.crystalfontz.com/product/cfa634tfhkc //--------------------------------------------------------------------------- //This is free and unencumbered software released into the public domain. // //Anyone is free to copy, modify, publish, use, compile, sell, or //distribute this software, either in source code form or as a compiled //binary, for any purpose, commercial or non-commercial, and by any //means. // //In jurisdictions that recognize copyright laws, the author or authors //of this software dedicate any and all copyright interest in the //software to the public domain. We make this dedication for the benefit //of the public at large and to the detriment of our heirs and //successors. We intend this dedication to be an overt act of //relinquishment in perpetuity of all present and future rights to this //software under copyright law. // //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, //EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF //MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. //IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR //OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, //ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR //OTHER DEALINGS IN THE SOFTWARE. // //For more information, please refer to //============================================================================ #include #include //I2C library //============================================================================ // ARD | Port | LCD | Color | Note // -----------+------+---------------|-------|---------------------------- // SCL | PC5 | I2C SCL Clock | Blue | (5K resistor pull up to 5v) // SDA | PC4 | I2C SDA Data | Green | (5K resistor pull up to 5v) //============================================================================ #define CFA_634_I2C_ADDRESS (42) #define USE_PRINTF //Saves ~1600 bytes //============================================================================ #ifdef USE_PRINTF // ref http://playground.arduino.cc/Main/Printf #include void SerPrintFF(const __FlashStringHelper *fmt, ... ) { char tmp[128]; // resulting string limited to 128 chars va_list args; va_start(args, fmt ); vsnprintf_P(tmp, 128, (const char *)fmt, args); va_end (args); Serial.print(tmp); } #endif //---------------------------------------------------------------------------- // ref http://scott.dd.com.au/wiki/Arduino_Static_Strings void SerialPrint_P(const char flash_string[]) { uint8_t c; for(;0x00 != (c = pgm_read_byte(flash_string)); flash_string++) { Serial.write(c); } } //============================================================================ //This does not depend on printf so it is small void I2C_String_XY(uint8_t col, uint8_t row, const __FlashStringHelper *flash_string ) { //Start the I2C transaction Wire.beginTransmission(CFA_634_I2C_ADDRESS); //Move the cursor to col,row Wire.write(17); Wire.write(col); Wire.write(row); //Pipe out the string, up to but not incuding the null terminator uint8_t this_character; //Get an editable copy of the flash string pointer const char *ptr; ptr=(const char *)flash_string; //Grab the next character from the flash string. If it is not //null, send it out. while(0 != (this_character=pgm_read_byte(ptr))) { Wire.write(this_character); //Point to the next character in RAM ptr++; } //Stop the I2C transaction Wire.endTransmission(); //Give the display a little time to deal with its new life situation. delay(3); } //============================================================================ void I2C_show_dec3_XY(uint8_t col, uint8_t row,uint8_t input) { //Start the I2C transaction Wire.beginTransmission(CFA_634_I2C_ADDRESS); //Move the cursor to col,row Wire.write(17); Wire.write(col); Wire.write(row); uint8_t digit; uint8_t no_blank; digit=input/100U; if(digit != 0U) { Wire.write(digit+(uint8_t)'0'); no_blank=1U; } else { Wire.write((uint8_t)' '); no_blank=0U; } input%=100U; /*lint --e(1960)*/ digit=input/10U; if((digit|no_blank) != 0U) { Wire.write(digit+(uint8_t)'0'); } else { Wire.write((uint8_t)' '); } Wire.write((input%10U) + (uint8_t)'0'); //Stop the I2C transaction Wire.endTransmission(); //Give the display some time to process the command delay(1); } //============================================================================ #ifdef USE_PRINTF // ref http://playground.arduino.cc/Main/Printf void I2CPrintFFXY(uint8_t col, uint8_t row, const __FlashStringHelper *fmt, ... ) { //Use variable arguments to format the string //this includes printf lib which is big char tmp[40]; // resulting string limited to 128 chars va_list args; va_start(args, fmt ); vsnprintf_P(tmp, 40, (const char *)fmt, args); va_end (args); //Send the formatted string to the display //Start the I2C transaction Wire.beginTransmission(CFA_634_I2C_ADDRESS); //Move the cursor to col,row Wire.write(17); Wire.write(col); Wire.write(row); //Pipe out the string, up to but not incuding the null terminator uint8_t this_character; //Get a pointer into the formatted string on the stack char *ptr; ptr=&tmp[0]; //Grab the next character from the flash string. If it is not //null, send it out. while(0 != (this_character=*ptr)) { Wire.write(this_character); //Point to the next character in RAM ptr++; } //Stop the I2C transaction Wire.endTransmission(); //Give the display some time to process the command delay(1); } #endif //============================================================================ void Bar_Graph(uint8_t graph_index, uint8_t style, uint8_t start_column, uint8_t end_column, uint8_t bar_length, uint8_t row) { Wire.beginTransmission(CFA_634_I2C_ADDRESS); #if 1 //Use built-in bar graph command //Bar command Wire.write(18); Wire.write(graph_index); Wire.write(style); Wire.write(start_column); Wire.write(end_column); Wire.write(bar_length); Wire.write(row); Wire.endTransmission(); //Give the display some time to process the command delay(5); #else //Or do it the old fashioned way. //The first character will be varaible width. //Set the second character to full width. Wire.beginTransmission(CFA_634_I2C_ADDRESS); Wire.write(25); //custom character command Wire.write((graph_index<<1)+1); Wire.write(style & 0x80 ? 0x7F : 0); //Top Line Wire.write(style & 0x40 ? 0x7F : 0); Wire.write(style & 0x10 ? 0x7F : 0); Wire.write(style & 0x20 ? 0x7F : 0); Wire.write(style & 0x08 ? 0x7F : 0); Wire.write(style & 0x04 ? 0x7F : 0); Wire.write(style & 0x02 ? 0x7F : 0); Wire.write(style & 0x01 ? 0x7F : 0); //Bottom Line Wire.endTransmission(); //Give the display some time to process the command delay(1); uint8_t i; //Write the characters in the bar area. The full width to the left, //the variable width character, then blanks. //Start the I2C transaction Wire.beginTransmission(CFA_634_I2C_ADDRESS); //Move the cursor to col,row Wire.write(17); Wire.write(start_column); Wire.write(row); //Fill in the solid characters. for(i=start_column;i<(bar_length/6);i++) //Second special character (solid); Wire.write(128+(graph_index<<1)+1); //Put in the partial, variable character Wire.write(128+(graph_index<<1)); //Fill the rest of the area with blanks for(i++;i<=end_column;i++) Wire.write(' '); Wire.endTransmission(); //Give the display some time to process the command delay(1); //Now set the partial bar width to the remainder uint8_t horizontal_mask; horizontal_mask = (0x3F << (6-(bar_length%6)) & 0x3F); Wire.beginTransmission(CFA_634_I2C_ADDRESS); Wire.write(25); //custom character command Wire.write((graph_index<<1)); Wire.write(style & 0x80 ? horizontal_mask : 0); //Top Line Wire.write(style & 0x40 ? horizontal_mask : 0); Wire.write(style & 0x10 ? horizontal_mask : 0); Wire.write(style & 0x20 ? horizontal_mask : 0); Wire.write(style & 0x08 ? horizontal_mask : 0); Wire.write(style & 0x04 ? horizontal_mask : 0); Wire.write(style & 0x02 ? horizontal_mask : 0); Wire.write(style & 0x01 ? horizontal_mask : 0); //Bottom Line Wire.endTransmission(); //Give the display some time to process the command delay(1); #endif } //============================================================================ void Bar_Graph_Demo(void) { //Build up the base screen //Start the I2C transaction Wire.beginTransmission(CFA_634_I2C_ADDRESS); //Clear the LCD Wire.write(12); Wire.endTransmission(); //Give the display some time to process the command delay(3); // "01234567890123456789" I2C_String_XY(0,0,F("Speed: 123 kph")); I2C_String_XY(0,2,F("Temperature: 123 oC ")); //Insert the superscript 0 for degree I2C_String_XY(17,2,F("\x1E\x01\x80")); uint8_t speed_position; uint8_t speed_direction; speed_position=0; speed_direction=1; uint16_t updates; for(updates=0;updates<1000;updates++) { #ifdef USE_PRINTF //Print the speed, numeric value I2CPrintFFXY(13,0,F("%3d"),speed_position); //Print the temp, numeric value I2CPrintFFXY(13,2,F("%3d"),80+(speed_position/6)); #else //Print the speed, numeric value I2C_show_dec3_XY(13,0,speed_position); ///Print the temp, numeric value I2C_show_dec3_XY(13,2,80+(speed_position/6)); #endif //Draw speed bar. 1px = 2KMH Bar_Graph(0, //graph_index 0x7E, //style 0, //start_column, 19, //end_column, speed_position/2, //bar_length, 1); //row //Draw Temp 1px = 1 deg C Bar_Graph(1, //graph_index 0x3C, //style 0, //start_column, 19, //end_column, 80+(speed_position/6), //bar_length, 3); //row if(speed_direction) { //speeding up if(speed_position<239) { speed_position++; } else { //start slowing down speed_direction=0; } } else { //slowing down if(48magenta <" // "to choose |red |" // "the color |pink |" // "e to set. |hot pink|" I2C_String_XY(0,0,F("Use \xDE\xE0 to ")); I2C_String_XY(0,1,F("to choose ")); I2C_String_XY(0,2,F("the color ")); I2C_String_XY(0,3,F("\x1E\x01\x1C to set. ")); } //---------------------------------------------------------------------------- void Menu_Demo_Set_Highlight_Line(uint8_t active_line) { uint8_t line; for(line=0;line<=3;line++) { I2C_String_XY(10,line,(line == active_line)?F("\x1E\x01\x10"):F("\xFE")); I2C_String_XY(19,line,(line == active_line)?F("\x1E\x01\x11"):F("\xFE")); } } //---------------------------------------------------------------------------- void Menu_Demo(void) { Menu_Demo_Base_screen(); //The item selected 0~21 uint8_t menu_position; uint8_t menu_direction; //The line the pointers are on 0~3 uint8_t pointer_position; menu_position=0; menu_direction=1; pointer_position=0; uint16_t updates; for(updates=0;updates<70;updates++) { Menu_Demo_Show_Choices(menu_position-pointer_position); Menu_Demo_Set_Highlight_Line(pointer_position); delay(400); if(menu_direction) { //moving down in the list if(menu_position < 21) { menu_position++; if(pointer_position<3) pointer_position++; } else { //start moving back up menu_direction=0; } } else { //moving up in the list if(0 < menu_position) { menu_position--; if(0