/* * LCDi2c4bit.cpp: part of the "VoluMaster(tm)" system * * An open-source arduino controller-based digital volume control and input/output selector * * * LICENSE * ------- * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "LCDi2c4bit.h" #include #include // not needed yet #include // needed for strlen() #include #include "WConstants.h" // all things wiring / arduino #include "config.h" extern uint8_t lcd_in_use_flag; #define OUTLINE_TOP 159 #define NO_OUTLINE_TOP 128 const uint8_t graph_horiz_line_pre[] = { NO_OUTLINE_TOP,128,159,159,159,159,128,NO_OUTLINE_TOP }; const uint8_t graph_horiz_line_post[] = { NO_OUTLINE_TOP,128,128,149,149,128,128,NO_OUTLINE_TOP }; // special start-char const uint8_t graph_start[] = { NO_OUTLINE_TOP,128,128,149,149,128,128,NO_OUTLINE_TOP }; const uint8_t graph_end[] = { NO_OUTLINE_TOP,128,159,159,159,159,128,NO_OUTLINE_TOP }; const uint8_t graph_b1[] = { NO_OUTLINE_TOP,128,144,149,149,144,128,NO_OUTLINE_TOP }; const uint8_t graph_b2[] = { NO_OUTLINE_TOP,128,152,157,157,152,128,NO_OUTLINE_TOP }; const uint8_t graph_b3[] = { NO_OUTLINE_TOP,128,156,157,157,156,128,NO_OUTLINE_TOP }; const uint8_t graph_b4[] = { NO_OUTLINE_TOP,128,158,159,159,158,128,NO_OUTLINE_TOP }; const uint8_t graph_b5[] = { NO_OUTLINE_TOP,128,159,159,159,159,128,NO_OUTLINE_TOP }; // these 2 can share the same 'cell' since only 1 is on at a time const uint8_t graph_play[] = { 136,140,142,143,142,140,136,128 }; const uint8_t graph_pause[] = { 155,155,155,155,155,155,155,128 }; /* * LCD graphics for bar-graphs is 'boxed' style */ // empty box const uint8_t boxed_graph_b0[] = { OUTLINE_TOP,128,128,128,128,128,128,OUTLINE_TOP }; // the graph values, inside a char-cell const uint8_t boxed_graph_b1[] = { OUTLINE_TOP,144,144,144,144,144,144,OUTLINE_TOP }; const uint8_t boxed_graph_b2[] = { OUTLINE_TOP,136,136,136,136,136,136,OUTLINE_TOP }; const uint8_t boxed_graph_b3[] = { OUTLINE_TOP,132,132,132,132,132,132,OUTLINE_TOP }; const uint8_t boxed_graph_b4[] = { OUTLINE_TOP,130,130,130,130,130,130,OUTLINE_TOP }; const uint8_t boxed_graph_b5[] = { OUTLINE_TOP,129,129,129,129,129,129,OUTLINE_TOP }; LCDI2C4Bit::LCDI2C4Bit( uint8_t devI2CAddress, uint8_t num_lines, uint8_t lcdwidth, uint8_t backlightPin ) { myNumLines = num_lines; myWidth = lcdwidth; myAddress = lcd_i2c_address = devI2CAddress; myBacklightPin = backlightPin; myInputKeysMask = 0x00; // default myBacklight_max = DEFAULT_BL_LEVEL; myBacklight_min = MIN_BL_LEVEL; backlight_currently_on = 1; // boolean one_second_counter_ts = 0; seconds = 0; lcd_inactivity_timeout = 4; // in seconds: how long do we keep the 'bright' setting before we fade backlight_admin = 0; // administratively set (enable auto timeout; normal mode) /* * setup and define our pins. usually a good idea toward the top of each program. */ pinMode(myBacklightPin, OUTPUT); // pwm backlight } void LCDI2C4Bit::SetInputKeysMask( uint8_t input_keys_mask ) { myInputKeysMask = input_keys_mask; } // TODO: use the separate 'mcp23xx' class void LCDI2C4Bit::SetMCPReg( uint8_t reg, uint8_t val ) { Wire.beginTransmission(lcd_i2c_address); Wire.send(reg); Wire.send(val); Wire.endTransmission(); } uint8_t LCDI2C4Bit::GetMCPReg( uint8_t reg ) { uint8_t val = 0; Wire.beginTransmission(lcd_i2c_address); Wire.send(reg); Wire.endTransmission(); val = Wire.requestFrom((uint8_t)lcd_i2c_address, (uint8_t)1); // read 1 byte if (Wire.available()) { val = Wire.receive(); } return val; } uint8_t LCDI2C4Bit::ReadInputKeys( void ) { uint8_t data; data = GetMCPReg(MCP_REG_GPIO); return data; } void LCDI2C4Bit::SendToLCD( uint8_t data ) { data |= dataPlusMask; SetMCPReg(MCP_REG_OLAT, data); delayMicroseconds(6); data ^= 0x80; // Enable bit ON on LCD SetMCPReg(MCP_REG_OLAT, data); delayMicroseconds(6); data ^= 0x80; // Enable bit OFF on LCD SetMCPReg(MCP_REG_OLAT, data); delayMicroseconds(6); } void LCDI2C4Bit::WriteLCDByte( uint8_t bdata ) { lcd_in_use_flag = 1; // set a mutex so that the IR isr won't do anything while we are doing lcd i/o SendToLCD( bdata >> 4 ); delayMicroseconds(6); SendToLCD( bdata & 0x0F ); delayMicroseconds(6); lcd_in_use_flag = 0; // clear that mutex (so that the IR isr CAN now do things) } void LCDI2C4Bit::init( void ) { lcd_in_use_flag = 1; // set a mutex so that the IR isr won't do anything while we are doing lcd i/o dataPlusMask = 0; SetMCPReg(MCP_REG_IOCON, 0x0C); delay(50); SetMCPReg(MCP_REG_IODIR, myInputKeysMask); delay(50); SetMCPReg(MCP_REG_GPPU, myInputKeysMask); delay(50); SendToLCD(0x03); delay(5); SendToLCD(0x03); delayMicroseconds(100); SendToLCD(0x03); delay(5); SendToLCD(0x02); WriteLCDByte(0x28); WriteLCDByte(0x08); WriteLCDByte(0x0C); // turn on, cursor off, no blinking delayMicroseconds(60); WriteLCDByte(0x01); // clear display delay(5); lcd_in_use_flag = 0; // clear that mutex (so that the IR isr CAN now do things) } void LCDI2C4Bit::backLight( uint8_t value ) { analogWrite(myBacklightPin, value); // 0..255 gets us 'dark' to 'very bright' } void LCDI2C4Bit::write( uint8_t value ) { dataPlusMask |= 0x10; // RS WriteLCDByte(value); dataPlusMask ^= 0x10; // RS } void LCDI2C4Bit::print( char value[] ) { for ( char *p = value; *p != 0; p++ ) { write(*p); } } void LCDI2C4Bit::clear( void ) { command(CMD_CLR); } void LCDI2C4Bit::cursorTo( uint8_t row, uint8_t col ) { int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; command(0x80 | (col + row_offsets[row])); } void LCDI2C4Bit::command( uint8_t command ) { // RS - leave low WriteLCDByte(command); //delayMicroseconds(500); delayMicroseconds(700); } void LCDI2C4Bit::LCD_send_string( const char *str, const uint8_t addr ) { // Send string at addr, if addr <> 0, or cursor position if addr == 0 if (addr != 0) { command(addr); } uint8_t i = 0; while (str[i] != 0) { write(str[i++]); } } void LCDI2C4Bit::LCD_cgram_load_normal_bargraph( void ) { uint8_t i; /* * load special graphic chars into 'cgram' */ command(0x40); // start off writing to CG RAM char 0 // slot-0 for (i=0; i<8; i++) { write(graph_play[i]); } // slot-1 for (i=0; i<8; i++) { write(graph_horiz_line_pre[i]); } // slot-2 for (i=0; i<8; i++) { write(graph_horiz_line_post[i]); } /* * 5 data point (graphic) bitmaps */ // slot-3 for (i=0; i<8; i++) { write(graph_b1[i]); } // slot-4 for (i=0; i<8; i++) { write(graph_b2[i]); } // slot-5 for (i=0; i<8; i++) { write(graph_b3[i]); } // slot-6 for (i=0; i<8; i++) { write(graph_b4[i]); } // slot-7 for (i=0; i<8; i++) { write(graph_b5[i]); } } void LCDI2C4Bit::LCD_cgram_load_boxed_bargraph( void ) { uint8_t i; /* * load special graphic chars into 'cgram' */ command(0x40); // start off writing to CG RAM char 0 // empty box for (i=0; i<8; i++) { write(boxed_graph_b0[i]); } // 5 data point bitmaps for (i=0; i<8; i++) { write(boxed_graph_b1[i]); } for (i=0; i<8; i++) { write(boxed_graph_b2[i]); } for (i=0; i<8; i++) { write(boxed_graph_b3[i]); } for (i=0; i<8; i++) { write(boxed_graph_b4[i]); } for (i=0; i<8; i++) { write(boxed_graph_b5[i]); } } void LCDI2C4Bit::clear_line( uint8_t line_num ) { command(line_num); for (uint8_t i=0; imyBacklight_min; bl--) { backLight(bl); // temporarily turn backlight entirely off (or go to its lowest 'low' setting) delay(2); } backlight_currently_on = 0; // flag it as now off } void LCDI2C4Bit::lcd_fade_backlight_complete_off( void ) { for (int bl=myBacklight_max; bl>0; bl--) { backLight(bl); // temporarily turn backlight entirely off (or go to its lowest 'low' setting) delay(2); } backlight_currently_on = 0; // flag it as now off } #if 0 // value is a float from (0.0 .. 1.0) (ie, 0-100% of fullscale) void draw_graphic_bar(char *dest_buf, float value, int total_bargraph_size) { byte i; int scaled_bit_pos; int scaled_char_pos; byte byte_graph_code; byte dest_buf_idx=0; float scaled_temp; // which char position gets the graph point? scaled_temp = value * (float)total_bargraph_size; scaled_char_pos = (int) scaled_temp; // truncate the fraction and keep the int value scaled_bit_pos = (int) (scaled_temp * (float)HORIZ_PIXELS_PER_CHAR); // (1) draw from 0 up to user 'lower limit' if (scaled_char_pos > 0) { for (i=0; ; i++) { if (i < scaled_char_pos) { dest_buf[dest_buf_idx++] = 1; // 'small dots' line style //lcd.write(1); // 'small dots' line style } else break; } } // (2) we're at the cell that needs the 'graphic' // which graphic (0..5) should be used? if (i < total_bargraph_size) { byte_graph_code = (scaled_bit_pos - (scaled_char_pos * HORIZ_PIXELS_PER_CHAR)); dest_buf[dest_buf_idx++] = byte_graph_code+3; // skipping over the first 2+1 magic chars //lcd.write(byte_graph_code+3); // skipping over the first 2+1 magic chars } // (3) draw postamble, if any if (i < total_bargraph_size) { for (i=scaled_char_pos+1; i < total_bargraph_size; i++) { dest_buf[dest_buf_idx++] = 2; // continue drawing the horiz_line, to the end, in 'small dots' //lcd.write(2); // continue drawing the horiz_line, to the end, in 'small dots' } } dest_buf[total_bargraph_size+1] = '\0'; } #endif