I'm working on a project that's going to have quite a few inputs, so I've been attempting to use an I2C LCD display and I2C port expander to help reduce the pin count. Datasheets for both devices are attached. I am using an Arduino UNO.
The wiring is fairly simple. A5 and A4 are connected to SCL and SDA of the port expander, which is then daisy-chained to the LCD display's SCL and SDA. All devices are powered by the UNO's 5V pin. A0-A2 are grounded on the port expander, RESET is tied high. INT goes to pin 2. The display has a jumper wire soldered onto R1, as required in the datahseet.
I have my sketch set up with two buttons attached to the port expander; one button clears the screen and prints a configuration menu, the other clears the screen and prints a different menu.
The problem I am having is that when I print the menus, letters are missing, and not always the same ones. This becomes particularly obvious when I press the same button repeatedly, causing the same menu to be printed. I can see the missing letters change.
I have two 10k resistors on each line as a pullup, giving 5k total.
I don't seem to have any problems at all reading the port expander, just printing to the display. I've tried adding delays after the endTransmission(), but it doesn't seem to help.
Frequency_Generator.ino
#include <SPI.h>
#include <Wire.h>
#include <avr/eeprom.h>
#include "NHD_I2C_Display.h"
#include "MCP23008.h"
#include "structs.h"
config_t configuration = { 4, 40, 2048 };
config_t old_config = configuration;
// ***************************************************
// EEPROM indexes and pointers
// ***************************************************
// Ring counter method from Atmel App Note 101
// High Endurance EEPROM Storage
#define EEPROM_START_ADDRESS 10
const uint8_t max_EEPROM_items = 150;
uint8_t ring_index = 0;
uint8_t ring_counter;
uint8_t* status_buffer = (uint8_t*)EEPROM_START_ADDRESS;
config_t* parameter_buffer = (config_t*) (status_buffer + (sizeof(status_buffer[0])*max_EEPROM_items));
// ***************************************************
// SPI stuff
// ***************************************************
#define DAC_SLAVE_SELECT 9
// ***************************************************
// Port Expander Buttons
// ***************************************************
gpio_button on_button = {0, HIGH, 0};
gpio_button off_button = {1, HIGH, 0};
const uint8_t INT_pin = 2;
// ***************************************************
// State Machine variables
// ***************************************************
typedef enum
{
tr_ANALOG, ANALOG_MENU, tr_DIGITAL_CLOCK, DIGITAL_CLOCK_MENU, tr_DIGITAL_PWM, DIGITAL_PWM_MENU, tr_CONFIG, CONFIG_MENU
} state_t;
state_t system_state = tr_CONFIG;
// ***************************************************
// Setup function
// ***************************************************
void setup()
{
Serial.begin(19200);
SPI.begin();
SPI.setClockDivider( SPI_CLOCK_DIV2 );
Wire.begin();
load_EEPROM_config();
// Set Port Expander configuration
pinMode( INT_pin, INPUT );
gpio_set_inputs( _BV(on_button.pin) | _BV(off_button.pin) );
gpio_set_pullups( _BV(on_button.pin) | _BV(off_button.pin) );
gpio_set_interrupt_on_change( _BV(on_button.pin) | _BV(off_button.pin) );
gpio_set_defualt_value( 0xFF );
gpio_set_interrupt_control( 0x00 );
gpio_set_configuration( 0x00 );
lcd_clear_screen();
lcd_set_backlight( configuration.backlight );
lcd_set_contrast( configuration.contrast );
}
// ***************************************************
// Loop function
// ***************************************************
void loop()
{
update_port_expander_buttons();
if( on_button.edge && on_button.state==HIGH )
system_state = tr_CONFIG;
if( off_button.edge && off_button.state==HIGH )
system_state = tr_ANALOG;
switch( system_state )
{
case tr_CONFIG:
init_CONFIG_menu();
break;
case tr_ANALOG:
init_ANALOG_menu();
break;
}
}
void load_EEPROM_config()
{
// Search for the right EEPROM pointer in the status buffer.
uint8_t match_found = 0;
while( ring_index<max_EEPROM_items && !match_found )
{
ring_index++;
match_found = (uint8_t)(eeprom_read_byte(&status_buffer[ring_index])-eeprom_read_byte(&status_buffer[ring_index-1])) != 1;
}
if( ring_index==max_EEPROM_items )
{
ring_index = 0;
ring_counter = eeprom_read_byte(&status_buffer[max_EEPROM_items-1]);
}
else
{
ring_counter = eeprom_read_byte(&status_buffer[ring_index-1]);
}
// Read config from EEPROM
eeprom_read_block( &configuration, ¶meter_buffer[ring_index], sizeof(config_t) );
// if the EEPROM starts out blank, load the default configuration.
if( configuration.backlight==0xFF && configuration.contrast==0xFF && configuration.dc_zero_offset==0xFFFF )
{
configuration.backlight = old_config.backlight;
configuration.contrast = old_config.contrast;
configuration.dc_zero_offset = old_config.dc_zero_offset;
}
else
{
old_config = configuration;
}
}
// ***************************************************
// update_EEPROM_config function
// ***************************************************
void update_EEPROM_config()
{
// If the new configuration is different from the old one...
// update the config present in the EEPROM.
if( configuration.backlight!=old_config.backlight || configuration.contrast!=old_config.contrast || configuration.dc_zero_offset!=old_config.dc_zero_offset )
{
ring_counter++;
eeprom_update_byte( &status_buffer[ring_index], ring_counter );
Serial.println( configuration.dc_zero_offset );
eeprom_update_block( &configuration, ¶meter_buffer[ring_index], sizeof(config_t) );
Serial.println( configuration.dc_zero_offset );
ring_index++;
if( ring_index==max_EEPROM_items )
{
ring_index = 0;
}
old_config = configuration;
}
}
void update_port_expander_buttons()
{
clear_port_expander_edge();
if( digitalRead(INT_pin) == LOW )
{
uint8_t buttons = gpio_read_port();
update_gpio_button( &on_button, buttons );
update_gpio_button( &off_button, buttons );
}
}
void clear_port_expander_edge()
{
on_button.edge = 0;
off_button.edge = 0;
}
void update_gpio_button( gpio_button* button, uint8_t port_state )
{
uint8_t current_state = (port_state&_BV(button->pin)) ? HIGH : LOW;
button->edge = current_state != button->state;
button->state = current_state;
}
void init_CONFIG_menu()
{
lcd_clear_screen();
lcd_set_cursor( 0, 0 );
lcd_print_string( "Configuration Menu" );
lcd_set_cursor( 0, 1 );
lcd_print_string( ("Backlight:") );
lcd_set_cursor( 0, 2 );
lcd_print_string( ("Contrast:") );
lcd_set_cursor( 0, 3 );
lcd_print_string( ("Zero Offset:") );
system_state = CONFIG_MENU;
}
void init_ANALOG_menu()
{
lcd_clear_screen();
lcd_set_cursor( 0, 0 );
lcd_print_string( "Analog:" );
lcd_set_cursor( 0, 1 );
lcd_print_string( "Freq:" );
lcd_set_cursor( 0, 2 );
lcd_print_string( "Amplitude:" );
lcd_set_cursor( 0, 3) ;
lcd_print_string( "DC Offset:" );
system_state = ANALOG_MENU;
}
I can post the code for the port expander too, but that doesn't seem to be causing any problems.
NHD-0420D3Z-FL-GBW-V3-29857.pdf (576 KB)
MCP23008 - 8-bit IO Expander with Serial Interface.pdf (295 KB)