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 (;;) {