/** Prints a character at the current cursor position. \param data The character to be displayed. This code is identical for all display buses and display types, because it just queues up the character. In case the buffer is full already it waits for a millisecond to allow data to be sent to the display, then it tries again. If it still fails then, it drops the character. This way we're fairly protected against data loss, still we guarantee to not hang forever. */ void display_writechar(uint8_t data) { if ( ! buf_canwrite(display)) { delay_ms(1); } if (buf_canwrite(display)) { buf_push(display, data); } }
/** Send a byte to the I2C partner. \param data The byte to be buffered/sent. \param last_byte Wether this is the last byte of a transmission. This implementation assumes to talk to mostly one communications client. To set the target, or to change it between transmissions, call i2c_init(). Unlike many other protocols (serial, SPI), I2C has an explicite transmission start and transmission end. Invoking code has to tell wether the given byte is the last byte of a transmission, so sending code can properly end it. This function has been tested to properly distinguish between individual transmissions separated by last_byte. Other than setting this flag, invoking code doesn't have to care about distinction, but may experience substantial delays (up to several milliseconds) if the bus is already busy with a distinct previous transmission. Data is buffered, so this returns quickly for small amounts of data. Large amounts don't get lost, but this function has to wait until sufficient previous data was sent. To avoid unexpected delays, invoking code can check for bus availability with i2c_busy(). Note that calling code has to send bytes quickly enough to not drain the buffer. It looks like the I2C protocol doesn't, unlike e.g. SPI, allow to pause sending without dropping the transmission. Positive of this limitation is, one can end a transmisson by simply not writing for a while, until it's sure the buffer became empty. */ void i2c_write(uint8_t data, uint8_t last_byte) { // Drop characters until transmission end. Transmissions to the display // start with a command byte, so sending truncated transmissions is harmful. if (i2c_state & I2C_ERROR) { if (last_byte) { i2c_state &= ~I2C_ERROR; } return; } while (i2c_should_end || ! buf_canwrite(send)) { delay_us(10); } if ( ! (i2c_state & I2C_MODE_BUSY)) { // No transmission ongoing, start one. i2c_state = I2C_MODE_SAWP; TWCR = (1<<TWINT)|(0<<TWEA)|(1<<TWSTA)|(0<<TWSTO)|(1<<TWEN)|(1<<TWIE); i2c_state |= I2C_MODE_BUSY; } ATOMIC_START buf_push(send, data); i2c_should_end = last_byte; ATOMIC_END }
/// send one character void serial_writechar(uint8_t data) { // check if interrupts are enabled if (SREG & MASK(SREG_I)) { // if they are, we should be ok to block since the tx buffer is emptied from an interrupt for (;buf_canwrite(tx) == 0;); buf_push(tx, data); } else { // interrupts are disabled- maybe we're in one? // anyway, instead of blocking, only write if we have room if (buf_canwrite(tx)) buf_push(tx, data); } // enable TX interrupt so we can send this character UCSR0B |= MASK(UDRIE0); }