From 619560c11203c714ae80ebf6508bcdf998f58c94 Mon Sep 17 00:00:00 2001 From: Ruslan Popov Date: Tue, 22 Sep 2015 20:05:02 +0300 Subject: [PATCH] I2C: initial support. This commit squashes in some fixes found after debugging on the topic branch. Test code by Traumflug, collected from Ruslan's code on the topic branch. Before, same as now without I2C: FLASH : 22408 bytes RAM : 1393 bytes EEPROM : 32 bytes Now with I2C: FLASH : 23224 bytes RAM : 2057 bytes EEPROM : 32 bytes This totals to some 800 bytes with a whole lot of test code, so implementation is pretty small :-) --- cpu-avr.c | 20 ++- i2c.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++ i2c.h | 110 +++++++++++++++ i2c_test.c | 189 +++++++++++++++++++++++++ mendel.c | 18 +++ 5 files changed, 725 insertions(+), 6 deletions(-) create mode 100644 i2c.c create mode 100644 i2c.h create mode 100644 i2c_test.c diff --git a/cpu-avr.c b/cpu-avr.c index d617d96..8f3dd27 100644 --- a/cpu-avr.c +++ b/cpu-avr.c @@ -18,16 +18,24 @@ */ void cpu_init() { #ifdef PRR - #if defined SPI - PRR = MASK(PRTWI) | MASK(PRADC); + #if defined I2C && defined SPI + PRR = MASK(PRADC); + #elif defined SPI + PRR = MASK(PRADC) | MASK(PRTWI); + #elif defined I2C + PRR = MASK(PRADC) | MASK(PRSPI); #else - PRR = MASK(PRTWI) | MASK(PRADC) | MASK(PRSPI); + PRR = MASK(PRADC) | MASK(PRTWI) | MASK(PRSPI); #endif #elif defined PRR0 - #if defined SPI - PRR0 = MASK(PRTWI) | MASK(PRADC); + #if defined I2C && defined SPI + PRR0 = MASK(PRADC); + #elif defined SPI + PRR0 = MASK(PRADC) | MASK(PRTWI); + #elif defined I2C + PRR0 = MASK(PRADC) | MASK(PRSPI); #else - PRR0 = MASK(PRTWI) | MASK(PRADC) | MASK(PRSPI); + PRR0 = MASK(PRADC) | MASK(PRTWI) | MASK(PRSPI); #endif #if defined(PRUSART3) // Don't use USART2 or USART3. Leave USART1 for GEN3 and derivatives. diff --git a/i2c.c b/i2c.c new file mode 100644 index 0000000..baaa033 --- /dev/null +++ b/i2c.c @@ -0,0 +1,394 @@ + +/** \file + \brief I2C / TWI subsystem + + "TWI", "Two Wire Interface", is Atmel's name for the (patented) I2C bus + system. I2C is technically pretty sophisticated, it also takes the current + state of these two wires as part of the protocol into account. Like SPI it's + a master/slave system with a clock signal on the wire. Unlike SPI, + communication partners aren't choosen by setting a pin, but by by + transferring an address byte before the actual data. + + Accordingly, code has to deal with states, transmissions have a start and + an end, and actions on the bus can result in different states, like success + or failure. + + avr-libc comes with good sample code: + + http://www.nongnu.org/avr-libc/examples/twitest/twitest.c + + For technical details see section 22 of atmega328 datasheet. +*/ + +#include "i2c.h" + +#ifdef I2C + +#include +#include + + +#if defined I2C_MASTER_MODE && defined I2C_SLAVE_MODE + #error Cant be I2C master and slave at the same time. +#endif + + +// Address of the device that is communicated with. +uint8_t i2c_address; +// State of TWI component of MCU. +volatile uint8_t i2c_state; +// Index into the send/receive buffer. +uint8_t i2c_index; +// Count of bytes to be sent. +uint8_t i2c_byte_count; + +#ifdef I2C_EEPROM_SUPPORT + // For SAWSARP mode (see ENHA in i2c.h). + uint8_t i2c_page_address[I2C_PAGE_ADDRESS_SIZE]; + // Index into the page address buffer. + uint8_t i2c_page_index; + // Count of bytes in page address. + uint8_t i2c_page_count; +#endif /* I2C_EEPROM_SUPPORT */ + +#ifdef I2C_SLAVE_MODE + uint8_t i2c_in_buffer[I2C_SLAVE_RX_BUFFER_SIZE]; + uint8_t i2c_out_buffer[I2C_SLAVE_TX_BUFFER_SIZE]; +#else + uint8_t *i2c_buffer; +#endif /* I2C_SLAVE_MODE */ + +I2C_HANDLER i2c_master_func = &i2c_do_nothing; +I2C_HANDLER i2c_slave_func = &i2c_do_nothing; +I2C_HANDLER i2c_error_func = &i2c_do_nothing; + + +void i2c_init(uint8_t address, I2C_HANDLER func) { + + i2c_address = address; + + #ifdef I2C_MASTER_MODE + #ifdef I2C_ENABLE_PULLUPS + I2C_DDR &= ~((1 << I2C_SCL_PIN) | (1 << I2C_SDA_PIN)); + I2C_PORT |= (1 << I2C_SCL_PIN) | (1 << I2C_SDA_PIN); + #endif /* I2C_ENABLE_PULLUPS */ + + i2c_master_func = func; + + /** + TWI Bit Rate register + SCL_freq = CPU_freq / (16 + 2 * TWBR) + See: page 235 of atmega328 datasheet. + */ + TWBR = ((F_CPU / I2C_BITRATE) - 16) / 2; + + /** + TWI Status Register + Lower two bits set the prescaler value. + See: page 236 of atmega328 datasheet. + */ + TWSR = 0x00; + #endif /* I2C_MASTER_MODE */ + + #ifdef I2C_SLAVE_MODE + i2c_slave_func = func; + TWAR = i2c_address; // We listen to broadcasts if lowest bit is set. + TWCR = (0< +#include "config_wrapper.h" +#include "delay.h" +#include "i2c.h" + + +#define DISPLAY_I2C_ADDRESS (0x3C << 1) + +static uint8_t display_init[] = { + 0x00, // Command marker. + 0xAE, // Display off. + 0xD5, 0x80, // Display clock divider (reset). + 0xA8, 0x1F, // 1/32 duty. + 0x40 | 0x00, // Start line (reset). + 0x20, 0x02, // Page addressing mode (reset). + 0xA0 | 0x00, // No segment remap (reset). + 0xC0 | 0x00, // Normal com pins mapping (reset). + 0xDA, 0x02, // Sequental without remap com pins. + 0x81, 0x7F, // Contrast (reset). + 0xDB, 0x20, // Vcomh (reset). + 0xD9, 0xF1, // Precharge period. + 0x8D, 0x14, // Charge pump. + 0xA6, // Positive display. + 0xA4, // Resume display. + 0xAF // Display on. +}; + +typedef struct { + uint8_t columns; + uint8_t data[4]; +} SYMBOL; + +SYMBOL font_8x4[] = { + {2, {0x00, 0x00, 0x00, 0x00}}, /* space */ + {3, {0x0C, 0x5E, 0x0C, 0x00}}, /* excl_mark */ + {3, {0x03, 0x00, 0x03, 0x00}}, /* quot_mark */ + {4, {0x74, 0x2E, 0x74, 0x2E}}, /* num_sign */ + {4, {0x24, 0xCA, 0x53, 0x24}}, /* dlr_sign */ + {4, {0x44, 0x30, 0x0C, 0x22}}, /* prc_sign */ + {4, {0x33, 0x4D, 0x22, 0x00}}, /* ampersand */ + {2, {0x02, 0x01, 0x00, 0x00}}, /* apostrophe */ + {2, {0x3E, 0x41, 0x00, 0x00}}, /* lparent */ + {2, {0x41, 0x3E, 0x00, 0x00}}, /* rparent */ + {3, {0x14, 0x3E, 0x14, 0x00}}, /* asterisk */ + {3, {0x08, 0x1C, 0x08, 0x00}}, /* plus_sign */ + {2, {0x40, 0x20, 0x00, 0x00}}, /* comma */ + {3, {0x08, 0x08, 0x08, 0x00}}, /* minus_sign */ + {1, {0x20, 0x00, 0x00, 0x00}}, /* dot */ + {3, {0x30, 0x0C, 0x03, 0x00}}, /* solidus */ + + {4, {0x3E, 0x41, 0x41, 0x3E}}, /* num_0 */ + {3, {0x44, 0x7F, 0x40, 0x00}}, /* num_1 */ + {4, {0x62, 0x51, 0x49, 0x46}}, /* num_2 */ + {4, {0x22, 0x49, 0x49, 0x36}}, /* num_3 */ + {4, {0x0F, 0x08, 0x08, 0x7E}}, /* num_4 */ + {4, {0x27, 0x45, 0x45, 0x39}}, /* num_5 */ + {4, {0x3E, 0x45, 0x45, 0x38}}, /* num_6 */ + {4, {0x01, 0x71, 0x09, 0x07}}, /* num_7 */ + {4, {0x36, 0x49, 0x49, 0x36}}, /* num_8 */ + {4, {0x26, 0x49, 0x49, 0x3E}}, /* num_9 */ + {1, {0x24, 0x00, 0x00, 0x00}}, /* colon */ + {2, {0x40, 0x24, 0x00, 0x00}}, /* semicolon */ + {3, {0x08, 0x14, 0x22, 0x00}}, /* less_sign */ + {3, {0x14, 0x14, 0x14, 0x00}}, /* equal_sign */ + {3, {0x22, 0x14, 0x08, 0x00}}, /* great_sign */ + {3, {0x02, 0x51, 0x0E, 0x00}}, /* ques_mark */ + + {4, {0x7E, 0x99, 0xA5, 0x3E}}, /* at */ + {3, {0x7E, 0x11, 0x7F, 0x00}}, /* a_cap */ + {3, {0x7F, 0x49, 0x36, 0x00}}, /* b_cap */ + {3, {0x3E, 0x41, 0x22, 0x00}}, /* c_cap */ + {3, {0x7F, 0x41, 0x3E, 0x00}}, /* d_cap */ + {3, {0x7F, 0x49, 0x41, 0x00}}, /* e_cap */ + {3, {0x7F, 0x09, 0x01, 0x00}}, /* f_cap */ + {3, {0x3E, 0x41, 0x71, 0x00}}, /* g_cap */ + {3, {0x7F, 0x08, 0x7F, 0x00}}, /* h_cap */ + {3, {0x41, 0x7F, 0x41, 0x00}}, /* i_cap */ + {3, {0x40, 0x41, 0x3F, 0x00}}, /* j_cap */ + {3, {0x7F, 0x0C, 0x73, 0x00}}, /* k_cap */ + {3, {0x7F, 0x40, 0x40, 0x00}}, /* l_cap */ + {3, {0x7F, 0x06, 0x7F, 0x00}}, /* m_cap */ + {3, {0x7F, 0x3C, 0x7F, 0x00}}, /* n_cap */ + {3, {0x3E, 0x41, 0x3E, 0x00}}, /* o_cap */ + + {3, {0x7F, 0x11, 0x0E, 0x00}}, /* p_cap */ + {3, {0x3E, 0x41, 0xBE, 0x00}}, /* q_cap */ + {3, {0x7F, 0x11, 0x6E, 0x00}}, /* r_cap */ + {3, {0x46, 0x49, 0x31, 0x00}}, /* s_cap */ + {3, {0x01, 0x7F, 0x01, 0x00}}, /* t_cap */ + {3, {0x3F, 0x40, 0x7F, 0x00}}, /* u_cap */ + {3, {0x3F, 0x40, 0x3F, 0x00}}, /* v_cap */ + {3, {0x7F, 0x30, 0x7F, 0x00}}, /* w_cap */ + {3, {0x73, 0x0C, 0x73, 0x00}}, /* x_cap */ + {3, {0x0F, 0x70, 0x0F, 0x00}}, /* y_cap */ + {3, {0x61, 0x5D, 0x47, 0x00}}, /* z_cap */ + {2, {0x7F, 0x41, 0x00, 0x00}}, /* lq_bracket */ + {3, {0x06, 0x18, 0x60, 0x00}}, /* rev_solidus */ + {2, {0x41, 0x7F, 0x00, 0x00}}, /* rq_bracket */ + {3, {0x02, 0x01, 0x02, 0x00}}, /* circumflex_accent */ + {3, {0x40, 0x40, 0x40, 0x00}}, /* low_line */ + + {2, {0x01, 0x02, 0x00, 0x00}}, /* grave_accent */ + {3, {0x30, 0x48, 0x78, 0x00}}, /* a_sml */ + {3, {0x7E, 0x48, 0x30, 0x00}}, /* b_sml */ + {3, {0x30, 0x48, 0x48, 0x00}}, /* c_sml */ + {3, {0x30, 0x48, 0x7E, 0x00}}, /* d_sml */ + {3, {0x30, 0x58, 0x58, 0x00}}, /* e_sml */ + {2, {0x7C, 0x12, 0x00, 0x00}}, /* f_sml */ + {3, {0x10, 0xA8, 0x70, 0x00}}, /* g_sml */ + {3, {0x7E, 0x08, 0x70, 0x00}}, /* h_sml */ + {1, {0x74, 0x00, 0x00, 0x00}}, /* i_sml */ + {2, {0x80, 0x7A, 0x00, 0x00}}, /* j_sml */ + {3, {0x7E, 0x10, 0x68, 0x00}}, /* k_sml */ + {2, {0x3E, 0x40, 0x00, 0x00}}, /* l_sml */ + {3, {0x78, 0x38, 0x70, 0x00}}, /* m_sml */ + {3, {0x78, 0x08, 0x70, 0x00}}, /* n_sml */ + {3, {0x30, 0x48, 0x30, 0x00}}, /* o_sml */ + + {3, {0xF8, 0x48, 0x30, 0x00}}, /* p_sml */ + {3, {0x30, 0x48, 0xF8, 0x00}}, /* q_sml */ + {2, {0x70, 0x08, 0x00, 0x00}}, /* r_sml */ + {3, {0x50, 0x58, 0x28, 0x00}}, /* s_sml */ + {2, {0x3E, 0x44, 0x00, 0x00}}, /* t_sml */ + {3, {0x38, 0x40, 0x78, 0x00}}, /* u_sml */ + {3, {0x38, 0x40, 0x38, 0x00}}, /* v_sml */ + {3, {0x78, 0x60, 0x78, 0x00}}, /* w_sml */ + {3, {0x68, 0x10, 0x68, 0x00}}, /* x_sml */ + {3, {0x18, 0xA0, 0x78, 0x00}}, /* y_sml */ + {3, {0x68, 0x68, 0x58, 0x00}}, /* z_sml */ + {3, {0x08, 0x36, 0x41, 0x00}}, /* left_curly_bracket */ + {1, {0x7F, 0x00, 0x00, 0x00}}, /* vertical_line */ + {3, {0x41, 0x36, 0x08, 0x00}}, /* rigth_curly_bracket */ + {4, {0x02, 0x01, 0x02, 0x01}}, /* tilde */ + {4, {0x00, 0x00, 0x00, 0x00}} /* del */ +}; + +#define FONT_SYMBOLS_SPACE 1 + + +static void i2c_test(void) { + static char* message = "Welcome to Teacup"; + static uint8_t block[128]; + uint8_t* pointer = block + 1; + + i2c_send(DISPLAY_I2C_ADDRESS, display_init, sizeof(display_init)); + delay_ms(250); + + /** + Setup cursor on display. + + "Welcome to Teacup" is 64 pixel columns wide, entire display is + 128 columns, so we offset by 32 columns to get it to the center. + */ + block[0] = 0x00; + // Line 1. + block[1] = 0xB0 | 1; + // Column 32. + block[2] = 0x00 | (32 & 0x0F); + block[3] = 0x10 | ((32 >> 4) & 0x0F); + i2c_send(DISPLAY_I2C_ADDRESS, block, 4); + delay_ms(50); + + // Render text to bitmap. + while (*message) { + SYMBOL symbol = font_8x4[(uint8_t)*message - 0x20]; + memcpy(pointer, symbol.data, symbol.columns); + pointer += symbol.columns + FONT_SYMBOLS_SPACE; + message++; + } + block[0] = 0x40; // Data marker. + i2c_send(DISPLAY_I2C_ADDRESS, block, pointer - block); +} + +#endif /* I2C_TEST */ diff --git a/mendel.c b/mendel.c index c79e4eb..36774bd 100644 --- a/mendel.c +++ b/mendel.c @@ -24,11 +24,19 @@ ctrl+d \endcode */ + #ifdef __AVR__ #include #endif #include "config_wrapper.h" +#ifdef I2C + // This is temporary, until display code is completed. + // It includes static i2c_test(), which is called in main(): + #define I2C_TEST + #include "i2c_test.c" + #undef I2C_TEST +#endif #include "cpu.h" #include "serial.h" #include "dda_queue.h" @@ -43,6 +51,7 @@ #include "clock.h" #include "intercom.h" #include "spi.h" +#include "i2c.h" #include "sd.h" #include "simulator.h" @@ -86,6 +95,10 @@ void init(void) { spi_init(); #endif + #ifdef I2C + i2c_init(DISPLAY_I2C_ADDRESS, i2c_do_nothing); + #endif + // set up timers timer_init(); @@ -134,6 +147,11 @@ int main (void) init(); + #ifdef I2C + // This is temporary, until display code is completed. + i2c_test(); + #endif + // main loop for (;;) {