Pergunta

I am developing an application in Atmel Studio 6 using the xMega32a4u. I'm using the TWI libraries provided by Atmel. Everything is going well for the most part.

Here is my issue: In order to update an OLED display I am using (SSD1306 controller, 128x32), the entire contents of the display RAM must be written immediately following the I2C START command, slave address, and control byte so the display knows to enter the data into the display RAM. If the control byte does not immediately precede the display RAM package, nothing works.

I am using a Saleae logic analyzer to verify that the bus is doing what it should.

Here is the function I am using to write the display:

void OLED_buffer(){ // Used to write contents of display buffer to OLED
    uint8_t data_array[513];
    data_array[0] = SSD1306_DATA_BYTE;
    for (int i=0;i<512;++i){
        data_array[i+1] = buffer[i];
    }
    OLED_command(SSD1306_SETLOWCOLUMN | 0x00);
    OLED_command(SSD1306_SETHIGHCOLUMN | 0x00);
    OLED_command(SSD1306_SETSTARTLINE | 0x00);
    twi_package_t buffer_send = {
        .chip = OLED_BUS_ADDRESS,
        .buffer = data_array,
        .length = 513
    };
    twi_master_write(&TWIC, &buffer_send);
}

Clearly, this is very inefficient as each call to this function recreates the entire array "buffer" into a new array "data_array," one element at a time. The point of this is to insert the control byte (SSD1306_DATA_BYTE = 0x40) into the array so that the entire "package" is sent at once, and the control byte is in the right place. I could make the original "buffer" array one element larger and add the control byte as the first element, to skip this process but that makes the size 513 rather than 512, and might mess with some of the text/graphical functions that manipulate this array and depend on it being the correct size.

Now, I thought I could write the code like this:

void OLED_buffer(){ // Used to write contents of display buffer to OLED
    uint8_t data_byte = SSD1306_DATA_BYTE;
    OLED_command(SSD1306_SETLOWCOLUMN | 0x00);
    OLED_command(SSD1306_SETHIGHCOLUMN | 0x00);
    OLED_command(SSD1306_SETSTARTLINE | 0x00);
    twi_package_t data_control_byte = {
        .chip = OLED_BUS_ADDRESS,
        .buffer = data_byte,
        .length = 1
    };
    twi_master_write(&TWIC, &data_control_byte);
    twi_package_t buffer_send = {
        .chip = OLED_BUS_ADDRESS,
        .buffer = buffer,
        .length = 512
    };
    twi_master_write(&TWIC, &buffer_send);
}
/*

That doesn't work. The first "twi_master_write" command sends a START, address, control, STOP. Then the next such command sends a START, address, data buffer, STOP. Because the control byte is missing from the latter transaction, this does not work. All I need is to insert a 0x40 byte between the address byte and the buffer array when it is sent over the I2C bus. twi_master_write is a function that is provided in the Atmel TWI libraries. I've tried to examine the libraries to figure out its inner workings, but I can't make sense of it.

Surely, instead of figuring out how to recreate a twi_write function to work the way I need, there is an easier way to add this preceding control byte? Ideally one that is not so wasteful of clock cycles as my first code example? Realistically the display still updates very fast, more than enough for my needs, but that does not change the fact this is inefficient code.

I appreciate any advice you all may have. Thanks in advance!

Foi útil?

Solução

How about having buffer and data_array pointing to the same uint8_t[513] array, but with buffer starting at its second element. Then you can continue to use buffer as you do today, but also use data_array directly without first having to copy all the elements from buffer.

uint8_t data_array[513];
uint8_t *buffer = &data_array[1];
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top