From 357b1c566a850221cd184b8923fc93faaf61d690 Mon Sep 17 00:00:00 2001 From: olikraus Date: Sat, 5 Dec 2015 22:43:30 +0100 Subject: [PATCH] software i2c --- csrc/u8x8.h | 12 +- csrc/u8x8_byte.c | 188 ++++++++++++++++++++++++++++ csrc/u8x8_d_ssd1306_128x64_noname.c | 2 +- csrc/u8x8_d_uc1701_dogs102.c | 4 +- sys/arduino/ssd1306_test/test.ino | 108 ++++++++-------- 5 files changed, 251 insertions(+), 63 deletions(-) diff --git a/csrc/u8x8.h b/csrc/u8x8.h index e831f4c4c..4cb57c4f4 100644 --- a/csrc/u8x8.h +++ b/csrc/u8x8.h @@ -163,7 +163,8 @@ struct u8x8_display_info_struct uint8_t sda_setup_time_ns; /* UC1601: 12ns */ /* the pulse width of the the clock signal, cycle time is twice this value */ /* max freq is 1/(2*sck_pulse_width_ns) */ - uint8_t sck_pulse_width_ns; /* UC1601: 15ns */ + /* AVR: below 70: DIV2, 8 MHz, >= 70 --> 4MHz clock (DIV4) */ + uint8_t sck_pulse_width_ns; /* UC1701: 50ns */ /* data takeover edge: 0=falling edge, 1=rising edge*/ /* initial default value for sck is low (0) for falling edge and high for rising edge */ @@ -205,7 +206,7 @@ struct u8x8_struct const uint8_t *font; u8x8_char_cb char_cb; /* procedure, which will be used to get the next char from the string */ uint8_t x_offset; /* copied from info struct, can be modified in flip mode */ - + uint8_t i2c_started; /* for i2c interface */ #ifdef U8X8_USE_PINS uint8_t pins[U8X8_PIN_CNT]; /* defines a pinlist: Mainly a list of pins for the Arduino Envionment, use U8X8_PIN_xxx to access */ #endif @@ -417,6 +418,7 @@ uint8_t u8x8_byte_SendByte(u8x8_t *u8x8, uint8_t byte) U8X8_NOINLINE; uint8_t u8x8_byte_SendBytes(u8x8_t *u8x8, uint8_t cnt, uint8_t *data) U8X8_NOINLINE; uint8_t u8x8_byte_8bit_sw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); +uint8_t u8x8_byte_ssd13xx_sw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); /*==========================================*/ @@ -436,8 +438,13 @@ uint8_t u8x8_byte_8bit_sw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void * #define U8X8_MSG_DELAY_10MICRO 42 #define U8X8_MSG_DELAY_100NANO 43 #define U8X8_MSG_DELAY_NANO 44 +/* delay of one i2c unit, should be 5us for 100K, and 1.25us for 400K */ +#define U8X8_MSG_DELAY_I2C 45 #define U8X8_MSG_GPIO(x) (64+(x)) +#ifdef U8X8_USE_PINS +#define u8x8_GetPinValue(u8x8, msg) ((u8x8)->pins[(msg)&0x3f]) +#endif #define U8X8_MSG_GPIO_D0 U8X8_MSG_GPIO(U8X8_PIN_D0) #define U8X8_MSG_GPIO_CLOCK U8X8_MSG_GPIO(U8X8_PIN_CLOCK) @@ -457,6 +464,7 @@ uint8_t u8x8_byte_8bit_sw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void * #define u8x8_gpio_Init(u8x8) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_AND_DELAY_INIT, 0, NULL )) + /* #define u8x8_gpio_SetDC(u8x8, v) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_DC, (v), NULL )) #define u8x8_gpio_SetCS(u8x8, v) ((u8x8)->gpio_and_delay_cb((u8x8), U8X8_MSG_GPIO_CS, (v), NULL )) diff --git a/csrc/u8x8_byte.c b/csrc/u8x8_byte.c index 37ea230c8..ef906b246 100644 --- a/csrc/u8x8_byte.c +++ b/csrc/u8x8_byte.c @@ -108,3 +108,191 @@ uint8_t u8x8_byte_8bit_sw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void * return 1; } +/*=========================================*/ + + +/* + software i2c, + ignores ACK response (which is anyway not provided by some displays) + also does not allow reading from the device +*/ +static void i2c_delay(u8x8_t *u8x8) U8X8_NOINLINE; +static void i2c_delay(u8x8_t *u8x8) +{ + //u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_10MICRO, u8x8->display_info->i2c_bus_clock_100kHz); + u8x8_gpio_Delay(u8x8, U8X8_MSG_DELAY_I2C, u8x8->display_info->i2c_bus_clock_100kHz); +} + +static void i2c_init(u8x8_t *u8x8) +{ + u8x8_gpio_SetClock(u8x8, 1); + u8x8_gpio_SetData(u8x8, 1); + + i2c_delay(u8x8); +} + +/* actually, the scl line is not observed, so this procedure does not return a value */ + +static void i2c_read_scl_and_delay(u8x8_t *u8x8) +{ + /* set as input (line will be high) */ + u8x8_gpio_SetClock(u8x8, 1); + + i2c_delay(u8x8); +} + +static void i2c_clear_scl(u8x8_t *u8x8) +{ + u8x8_gpio_SetClock(u8x8, 0); +} + +static void i2c_read_sda(u8x8_t *u8x8) +{ + /* set as input (line will be high) */ + u8x8_gpio_SetData(u8x8, 1); +} + +static void i2c_clear_sda(u8x8_t *u8x8) +{ + /* set open collector and drive low */ + u8x8_gpio_SetData(u8x8, 0); +} + +static void i2c_start(u8x8_t *u8x8) +{ + if ( u8x8->i2c_started != 0 ) + { + /* if already started: do restart */ + i2c_read_sda(u8x8); /* SDA = 1 */ + i2c_delay(u8x8); + i2c_read_scl_and_delay(u8x8); + } + i2c_read_sda(u8x8); + /* send the start condition, both lines go from 1 to 0 */ + i2c_clear_sda(u8x8); + i2c_delay(u8x8); + i2c_clear_scl(u8x8); + u8x8->i2c_started = 1; +} + + +static void i2c_stop(u8x8_t *u8x8) +{ + /* set SDA to 0 */ + i2c_clear_sda(u8x8); + i2c_delay(u8x8); + + /* now release all lines */ + i2c_read_scl_and_delay(u8x8); + + /* set SDA to 1 */ + i2c_read_sda(u8x8); + i2c_delay(u8x8); + u8x8->i2c_started = 0; +} + +static void i2c_write_bit(u8x8_t *u8x8, uint8_t val) +{ + if (val) + i2c_read_sda(u8x8); + else + i2c_clear_sda(u8x8); + + i2c_delay(u8x8); + i2c_read_scl_and_delay(u8x8); + i2c_clear_scl(u8x8); +} + +static void i2c_read_bit(u8x8_t *u8x8) +{ + //uint8_t val; + /* do not drive SDA */ + i2c_read_sda(u8x8); + i2c_delay(u8x8); + i2c_read_scl_and_delay(u8x8); + i2c_read_sda(u8x8); + i2c_delay(u8x8); + i2c_clear_scl(u8x8); + //return val; +} + +static void i2c_write_byte(u8x8_t *u8x8, uint8_t b) +{ + i2c_write_bit(u8x8, b & 128); + i2c_write_bit(u8x8, b & 64); + i2c_write_bit(u8x8, b & 32); + i2c_write_bit(u8x8, b & 16); + i2c_write_bit(u8x8, b & 8); + i2c_write_bit(u8x8, b & 4); + i2c_write_bit(u8x8, b & 2); + i2c_write_bit(u8x8, b & 1); + + /* read ack from client */ + /* 0: ack was given by client */ + /* 1: nothing happend during ack cycle */ + i2c_read_bit(u8x8); +} + + + + +uint8_t u8x8_byte_ssd13xx_sw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) +{ + uint8_t *data; + static uint8_t last_dc = 0; + static uint8_t is_send_dc = 0; + + switch(msg) + { + case U8X8_MSG_BYTE_SEND: + data = (uint8_t *)arg_ptr; + + if ( is_send_dc != 0 ) + { + + i2c_start(u8x8); + i2c_write_byte(u8x8, 0x078); /* write slave adr and read/write bit */ + + if ( last_dc == 0 ) + i2c_write_byte(u8x8, 0); + else + i2c_write_byte(u8x8, 0x040); + is_send_dc = 0; + } + + while( arg_int > 0 ) + { + i2c_write_byte(u8x8, *data); + data++; + arg_int--; + } + + break; + + case U8X8_MSG_BYTE_INIT: + i2c_init(u8x8); + break; + case U8X8_MSG_BYTE_SET_DC: + if ( last_dc != arg_int ) + { + last_dc = arg_int; + is_send_dc = 1; + } + break; + case U8X8_MSG_BYTE_START_TRANSFER: + last_dc = 0; + is_send_dc = 1; + break; + case U8X8_MSG_BYTE_END_TRANSFER: + i2c_stop(u8x8); + break; + case U8X8_MSG_BYTE_SET_I2C_ADR: + break; + case U8X8_MSG_BYTE_SET_DEVICE: + break; + default: + return 0; + } + return 1; +} + diff --git a/csrc/u8x8_d_ssd1306_128x64_noname.c b/csrc/u8x8_d_ssd1306_128x64_noname.c index 90d70b39c..719a73064 100644 --- a/csrc/u8x8_d_ssd1306_128x64_noname.c +++ b/csrc/u8x8_d_ssd1306_128x64_noname.c @@ -80,7 +80,7 @@ static const u8x8_display_info_t u8x8_ssd1306_128x64_noname_display_info = /* reset_pulse_width_ms = */ 100, /* SSD1306: 3 us */ /* post_reset_wait_ms = */ 100, /* far east OLEDs need much longer setup time */ /* sda_setup_time_ns = */ 50, /* SSD1306: 15ns, but cycle time is 100ns, so use 100/2 */ - /* sck_pulse_width_ns = */ 50, /* SSD1306: 20ns, but cycle time is 100ns, so use 100/2 */ + /* sck_pulse_width_ns = */ 50, /* SSD1306: 20ns, but cycle time is 100ns, so use 100/2, AVR: below 70: 8 MHz, >= 70 --> 4MHz clock */ /* sck_takeover_edge = */ 1, /* rising edge */ /* i2c_bus_clock_100kHz = */ 4, /* data_setup_time_ns = */ 40, diff --git a/csrc/u8x8_d_uc1701_dogs102.c b/csrc/u8x8_d_uc1701_dogs102.c index 524c70e33..63a1b7ee5 100644 --- a/csrc/u8x8_d_uc1701_dogs102.c +++ b/csrc/u8x8_d_uc1701_dogs102.c @@ -75,8 +75,8 @@ static const u8x8_display_info_t u8x8_uc1701_display_info = /* pre_chip_disable_wait_ns = */ 5, /* reset_pulse_width_ms = */ 1, /* post_reset_wait_ms = */ 6, - /* sda_setup_time_ns = */ 15, /* if this is smaller than sck_pulse_width_ns, use sck_pulse_width_ns, so use 15 instead of 12*/ - /* sck_pulse_width_ns = */ 15, + /* sda_setup_time_ns = */ 12, + /* sck_pulse_width_ns = */ 75, /* half of cycle time (100ns according to datasheet), AVR: below 70: 8 MHz, >= 70 --> 4MHz clock */ /* sck_takeover_edge = */ 1, /* rising edge */ /* i2c_bus_clock_100kHz = */ 37, /* data_setup_time_ns = */ 30, diff --git a/sys/arduino/ssd1306_test/test.ino b/sys/arduino/ssd1306_test/test.ino index 63c9c6dc5..61a70a6b2 100644 --- a/sys/arduino/ssd1306_test/test.ino +++ b/sys/arduino/ssd1306_test/test.ino @@ -16,44 +16,29 @@ extern "C" uint8_t u8x8_gpio_and_delay_arduino(u8x8_t *u8x8, uint8_t msg, uint8_ switch(msg) { case U8X8_MSG_GPIO_AND_DELAY_INIT: - /* new code */ + for( i = 0; i < U8X8_PIN_CNT; i++ ) if ( u8x8->pins[i] != U8X8_PIN_NONE ) pinMode(u8x8->pins[i], OUTPUT); - /* old code, can be removed */ - pinMode(8, OUTPUT); - pinMode(9, OUTPUT); - pinMode(10, OUTPUT); - pinMode(11, OUTPUT); - pinMode(13, OUTPUT); break; case U8X8_MSG_DELAY_MILLI: delay(arg_int); break; - - case U8X8_MSG_GPIO_DC: - digitalWrite(9, arg_int); - break; - - case U8X8_MSG_GPIO_CS: - digitalWrite(10, arg_int); - break; - - case U8X8_MSG_GPIO_RESET: - digitalWrite(8, arg_int); - break; - - case U8X8_MSG_GPIO_CLOCK: - digitalWrite(13, arg_int); - break; - - case U8X8_MSG_GPIO_DATA: - digitalWrite(11, arg_int); + case U8X8_MSG_DELAY_I2C: + /* arg_int is 1 or 4: 100KHz (5us) or 400KHz (1.25us) */ + delayMicroseconds(arg_int<=2?5:2); + break; + default: + if ( msg >= U8X8_MSG_GPIO(0) ) + { + i = u8x8_GetPinValue(u8x8, msg); + if ( i != U8X8_PIN_NONE ) + digitalWrite(i, arg_int); break; + } - default: return 0; } return 1; @@ -68,7 +53,6 @@ extern "C" uint8_t u8x8_byte_arduino_hw_spi(u8x8_t *u8g2, uint8_t msg, uint8_t a switch(msg) { case U8X8_MSG_BYTE_SEND: - data = (uint8_t *)arg_ptr; while( arg_int > 0 ) { @@ -83,24 +67,35 @@ extern "C" uint8_t u8x8_byte_arduino_hw_spi(u8x8_t *u8g2, uint8_t msg, uint8_t a /* no wait required here */ /* for SPI: setup correct level of the clock signal */ + pinMode(11, OUTPUT); + pinMode(13, OUTPUT); digitalWrite(13, u8g2->display_info->sck_takeover_edge); break; + case U8X8_MSG_BYTE_SET_DC: u8x8_gpio_SetDC(u8g2, arg_int); break; + case U8X8_MSG_BYTE_START_TRANSFER: SPI.begin(); - SPI.setClockDivider( SPI_CLOCK_DIV2 ); + + if ( u8g2->display_info->sck_pulse_width_ns < 70 ) + SPI.setClockDivider( SPI_CLOCK_DIV2 ); + else + SPI.setClockDivider( SPI_CLOCK_DIV4 ); SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); u8x8_gpio_SetCS(u8g2, u8g2->display_info->chip_enable_level); u8g2->gpio_and_delay_cb(u8g2, U8X8_MSG_DELAY_NANO, u8g2->display_info->post_chip_enable_wait_ns, NULL); break; - case U8X8_MSG_BYTE_END_TRANSFER: + + case U8X8_MSG_BYTE_END_TRANSFER: u8g2->gpio_and_delay_cb(u8g2, U8X8_MSG_DELAY_NANO, u8g2->display_info->pre_chip_disable_wait_ns, NULL); u8x8_gpio_SetCS(u8g2, u8g2->display_info->chip_disable_level); + SPI.end(); + break; case U8X8_MSG_BYTE_SET_I2C_ADR: break; @@ -187,42 +182,31 @@ void u8x8_Setup_HW_4Wire_SPI(u8x8_t *u8x8, u8x8_msg_cb display_cb, uint8_t cs, u /* setup display info */ u8x8_display_Setup(u8x8); - } -/*=============================================*/ -/* display setup procedures */ - -void u8x8_Setup_UC1701_DOGS102(u8x8_t *u8x8) +void u8x8_Setup_SW_SSD13xx_I2C(u8x8_t *u8x8, u8x8_msg_cb display_cb, uint8_t clock, uint8_t data, uint8_t reset) { /* setup defaults and reset pins to U8X8_PIN_NONE */ u8x8_SetupDefaults(u8x8); + /* assign individual pin values (only for ARDUINO, if pin_list is available) */ + u8x8_SetPin(u8x8, U8X8_PIN_CLOCK, clock); + u8x8_SetPin(u8x8, U8X8_PIN_DATA, data); + u8x8_SetPin(u8x8, U8X8_PIN_RESET, reset); + /* setup specific callbacks */ - u8x8->display_cb = u8x8_d_uc1701_dogs102; + u8x8->display_cb = display_cb; u8x8->cad_cb = u8x8_cad_001; - u8x8->byte_cb = u8x8_byte_8bit_sw_spi; + u8x8->byte_cb = u8x8_byte_ssd13xx_sw_i2c; u8x8->gpio_and_delay_cb = u8x8_gpio_and_delay_arduino; /* setup display info */ u8x8_display_Setup(u8x8); } -void u8x8_Setup_SSD1306_128x64_NONAME(u8x8_t *u8x8) -{ - /* setup defaults and reset pins to U8X8_PIN_NONE */ - u8x8_SetupDefaults(u8x8); - - /* setup specific callbacks */ - u8x8->display_cb = u8x8_d_ssd1306_128x64_noname; - u8x8->cad_cb = u8x8_cad_001; - //u8x8->byte_cb = u8x8_byte_arduino_hw_spi; - u8x8->byte_cb = u8x8_byte_8bit_sw_spi; - u8x8->gpio_and_delay_cb = u8x8_gpio_and_delay_arduino; - - /* setup display info */ - u8x8_display_Setup(u8x8); -} + +/*=============================================*/ +/* display setup procedures */ u8x8_t u8x8; @@ -230,12 +214,19 @@ uint8_t tile[8] = { 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0 }; void setup(void) { - //u8x8_Setup_UC1701_DOGS102(&u8x8); - //u8x8_Setup_SSD1306_128x64_NONAME(&u8x8); + pinMode(7, OUTPUT); + pinMode(9, OUTPUT); + pinMode(10, OUTPUT); + digitalWrite(7, 0); // default output in I2C mode for the SSD1306 test shield + digitalWrite(9, 0); // default output in I2C mode for the SSD1306 test shield: set the i2c adr to 0 + digitalWrite(10, 0); // default output in I2C mode for the SSD1306 test shield + //u8x8_Setup_SW_4Wire_SPI(&u8x8, u8x8_d_ssd1306_128x64_noname, 13, 11, 10, 9, 8); - u8x8_Setup_HW_4Wire_SPI(&u8x8, u8x8_d_ssd1306_128x64_noname, 10, 9, 8); // works!! + u8x8_Setup_HW_4Wire_SPI(&u8x8, u8x8_d_ssd1306_128x64_noname, 10, 9, 8); + //u8x8_Setup_SW_SSD13xx_I2C(&u8x8, u8x8_d_ssd1306_128x64_noname, 13, 11, 8); + //u8x8_Setup_SW_4Wire_SPI(&u8x8, u8x8_d_uc1701_dogs102, 13, 11, 10, 9, 8); - //u8x8_Setup_HW_4Wire_SPI(&u8x8, u8x8_d_uc1701_dogs102, 10, 9, 8); // broken???? does not work + //u8x8_Setup_HW_4Wire_SPI(&u8x8, u8x8_d_uc1701_dogs102, 10, 9, 8); } void loop(void) @@ -243,6 +234,7 @@ void loop(void) u8x8_display_Init(&u8x8); //u8x8_display_SetFlipMode(&u8x8, 1); + //digitalWrite(9, 0); // default output in I2C mode for the SSD1306 test shield: set the i2c adr to 0 for(;;) { @@ -250,7 +242,7 @@ void loop(void) u8x8_display_SetPowerSave(&u8x8, 0); //u8x8_display_SetContrast(&u8x8, 10); - delay(500); + //delay(500); u8x8_SetFont(&u8x8, u8x8_font_chroma48medium8_r); u8x8_DrawString(&u8x8, 0, 0, "Hello World"); @@ -263,7 +255,7 @@ void loop(void) u8x8_display_DrawTile(&u8x8, 5, 5, 1, tile); u8x8_display_DrawTile(&u8x8, 6, 6, 1, tile); - delay(2000); + //delay(2000); } }