diff --git a/Makefile b/Makefile index c81a352..9a5c223 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,10 @@ TARGET = main LIBDIR = lib # # Type of microcontroller -DEVICE = atmega16 +DEVICE = atmega328p # # Frequency -FCPU = 16000000 +FCPU = 8000000 # # Optimization OPTIMIZE = Os @@ -66,7 +66,7 @@ OBJECTS = $(SOURCES:.c=.o) AVRDUDE = avrdude # # AVRDUDE DEVICE -AVRDUDE_MMCU = m16 +AVRDUDE_MMCU = m328p # # AVRDUDE PORT AVRDUDE_PORT = /dev/ttyUSB0 diff --git a/lib/ssd1306.c b/lib/ssd1306.c index d419fcb..bd814a1 100644 --- a/lib/ssd1306.c +++ b/lib/ssd1306.c @@ -7,16 +7,20 @@ * * @author Marian Hrinko * @datum 06.10.2020 + * @update 19.07.2021 * @file ssd1306.c - * @tested AVR Atmega16, ATmega8, Atmega328 + * @tested AVR Atmega328 * * @depend font.h, twi.h * ---------------------------------------------------------------+ + * @descr Version 1.0 -> applicable for 1 display + * Version 2.0 -> applicable for more than 1 display + * ---------------------------------------------------------------+ * @usage Basic Setup for OLED Display */ - #include +#include #include "font.h" #include "twi.h" #include "ssd1306.h" @@ -150,6 +154,9 @@ const uint8_t INIT_SSD1306[] PROGMEM = { 0, SSD1306_DISPLAY_ON }; +// @var array Chache memory Lcd 8 * 128 = 1024 +static char cacheMemLcd[CACHE_SIZE_MEM]; + // @var global - set area unsigned int set_area = (END_PAGE_ADDR - START_PAGE_ADDR + 1) * (END_COLUMN_ADDR - START_COLUMN_ADDR + 1); // @var global - cache index column @@ -160,30 +167,30 @@ unsigned short int indexPage = START_PAGE_ADDR; /** * @desc SSD1306 Init * - * @param void + * @param uint8_t address * - * @return char + * @return uint8_t */ -char SSD1306_Init (void) +uint8_t SSD1306_Init (uint8_t address) { // variables const uint8_t *commands = INIT_SSD1306; // number of commands unsigned short int no_of_commands = pgm_read_byte(commands++); // argument - char no_of_arguments; + uint8_t no_of_arguments; // command - char command; + uint8_t command; // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: Init // ------------------------- - TWI_Init(); + TWI_Init (); // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -194,13 +201,13 @@ char SSD1306_Init (void) while (no_of_commands) { // number of arguments - no_of_arguments = pgm_read_byte(commands++); + no_of_arguments = pgm_read_byte (commands++); // command - command = pgm_read_byte(commands++); + command = pgm_read_byte (commands++); // send command // ------------------------- - status = SSD1306_Send_Command(command); + status = SSD1306_Send_Command (command); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -211,7 +218,7 @@ char SSD1306_Init (void) // ------------------------- while (no_of_arguments--) { // send command - status = SSD1306_Send_Command(pgm_read_byte(commands++)); + status = SSD1306_Send_Command (pgm_read_byte(commands++)); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -224,7 +231,7 @@ char SSD1306_Init (void) // TWI: Stop // ------------------------- - TWI_Stop(); + TWI_Stop (); // success return SSD1306_SUCCESS; @@ -233,18 +240,18 @@ char SSD1306_Init (void) /** * @desc SSD1306 Send Start and SLAW request * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ -char SSD1306_Send_StartAndSLAW (void) +uint8_t SSD1306_Send_StartAndSLAW (uint8_t address) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start // ------------------------- - status = TWI_MT_Start(); + status = TWI_MT_Start (); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -253,7 +260,7 @@ char SSD1306_Send_StartAndSLAW (void) // TWI: send SLAW // ------------------------- - status = TWI_MT_Send_SLAW(SSD1306_ADDRESS); + status = TWI_MT_Send_SLAW (address); // request - send SLAW if (SSD1306_SUCCESS != status) { // error @@ -267,18 +274,18 @@ char SSD1306_Send_StartAndSLAW (void) /** * @desc SSD1306 Send command * - * @param char + * @param uint8_t * - * @return char + * @return uint8_t */ -char SSD1306_Send_Command (char command) +uint8_t SSD1306_Send_Command (uint8_t command) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // send control byte // ------------------------- - status = TWI_MT_Send_Data(SSD1306_COMMAND); + status = TWI_MT_Send_Data (SSD1306_COMMAND); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -287,7 +294,7 @@ char SSD1306_Send_Command (char command) // send command // ------------------------- - status = TWI_MT_Send_Data(command); + status = TWI_MT_Send_Data (command); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -301,18 +308,18 @@ char SSD1306_Send_Command (char command) /** * @desc SSD1306 Normal Colors * - * @param void + * @param uint8_t address * - * @return char + * @return uint8_t */ -char SSD1306_NormalScreen (void) +uint8_t SSD1306_NormalScreen (uint8_t address) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -321,7 +328,7 @@ char SSD1306_NormalScreen (void) // send command // ------------------------- - status = SSD1306_Send_Command(SSD1306_DIS_NORMAL); + status = SSD1306_Send_Command (SSD1306_DIS_NORMAL); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -335,18 +342,18 @@ char SSD1306_NormalScreen (void) /** * @desc SSD1306 Inverse Colors * - * @param void + * @param uint8_t address * - * @return char + * @return uint8_t */ -char SSD1306_InverseScreen (void) +uint8_t SSD1306_InverseScreen (uint8_t address) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -355,7 +362,7 @@ char SSD1306_InverseScreen (void) // send command // ------------------------- - status = SSD1306_Send_Command(SSD1306_DIS_INVERSE); + status = SSD1306_Send_Command (SSD1306_DIS_INVERSE); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -369,61 +376,20 @@ char SSD1306_InverseScreen (void) /** * @desc SSD1306 Update Screen On * - * @param void + * @param uint8_t address * - * @return char + * @return uint8_t */ -char SSD1306_UpdateScreen (void) +uint8_t SSD1306_UpdateScreen (uint8_t address) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; + // init i + uint16_t i = 0; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // send command - // ------------------------- - status = SSD1306_Send_Command(SSD1306_DISPLAY_ON); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // success - return SSD1306_SUCCESS; -} - -/** - * @desc SSD1306 Send Command - * - * @param void - * - * @return char - */ -char SSD1306_ClearScreen (void) -{ - // init status - char status = INIT_STATUS; - short int i = 0; - - // update - null col, increment page - status = SSD1306_SetPosition(0, 0); - // request - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // TWI: start & SLAW - // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -432,7 +398,70 @@ char SSD1306_ClearScreen (void) // control byte data stream // ------------------------- - status = TWI_MT_Send_Data(SSD1306_DATA_STREAM); + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // erase whole area + // ------------------------- + while (i < CACHE_SIZE_MEM) { + // send null data + status = TWI_MT_Send_Data (cacheMemLcd[i]); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // increment + i++; + } + + // stop TWI + TWI_Stop (); + + // success + return SSD1306_SUCCESS; +} + +/** + * @desc SSD1306 Send Command + * + * @param uint8_t address + * + * @return void + */ +void SSD1306_ClearScreen (uint8_t address) +{ + // init status + uint8_t status = INIT_STATUS; + //short int i = 0; + + // null cache memory lcd + memset (cacheMemLcd, 0x00, CACHE_SIZE_MEM); +/* + // update - null col, increment page + status = SSD1306_SetPosition (address, 0, 0); + // request + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // TWI: start & SLAW + // ------------------------- + status = SSD1306_Send_StartAndSLAW (address); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // control byte data stream + // ------------------------- + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -443,7 +472,7 @@ char SSD1306_ClearScreen (void) // ------------------------- while (i < set_area) { // send null data - status = TWI_MT_Send_Data(0x00); + status = TWI_MT_Send_Data (0x00); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -453,10 +482,9 @@ char SSD1306_ClearScreen (void) i++; } - // stop TWI - TWI_Stop(); - + TWI_Stop (); +*/ // success return SSD1306_SUCCESS; } @@ -464,20 +492,21 @@ char SSD1306_ClearScreen (void) /** * @desc SSD1306 Set Poisition * - * @param char - * @param char + * @param uint8_t address + * @param uint8_t + * @param uint8_t * - * @return char + * @return uint8_t */ -char SSD1306_SetPosition (char x, char y) +uint8_t SSD1306_SetPosition (uint8_t address, uint8_t x, uint8_t y) { // variables - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); - // request - start TWI + status = SSD1306_Send_StartAndSLAW (address); + // request success if (SSD1306_SUCCESS != status) { // error return status; @@ -487,24 +516,24 @@ char SSD1306_SetPosition (char x, char y) // *************************************************** // set column addr // ------------------------- - status = SSD1306_Send_Command(SSD1306_SET_COLUMN_ADDR); - // request - start TWI + status = SSD1306_Send_Command (SSD1306_SET_COLUMN_ADDR); + // request success if (SSD1306_SUCCESS != status) { // error return status; } // start COLUMN // ------------------------- - status = SSD1306_Send_Command(x); - // request - start TWI + status = SSD1306_Send_Command (x); + // request success if (SSD1306_SUCCESS != status) { // error return status; } // end COLUMN // ------------------------- - status = SSD1306_Send_Command(END_COLUMN_ADDR); - // request - start TWI + status = SSD1306_Send_Command (END_COLUMN_ADDR); + // request success if (SSD1306_SUCCESS != status) { // error return status; @@ -516,7 +545,7 @@ char SSD1306_SetPosition (char x, char y) // *************************************************** // set page addr // ------------------------- - status = SSD1306_Send_Command(SSD1306_SET_PAGE_ADDR); + status = SSD1306_Send_Command (SSD1306_SET_PAGE_ADDR); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -524,16 +553,16 @@ char SSD1306_SetPosition (char x, char y) } // start PAGE // ------------------------- - status = SSD1306_Send_Command(y); - // request - start TWI + status = SSD1306_Send_Command (y); + // request success if (SSD1306_SUCCESS != status) { // error return status; } // end PAGE // ------------------------- - status = SSD1306_Send_Command(END_PAGE_ADDR); - // request - start TWI + status = SSD1306_Send_Command (END_PAGE_ADDR); + // request success if (SSD1306_SUCCESS != status) { // error return status; @@ -542,7 +571,7 @@ char SSD1306_SetPosition (char x, char y) indexPage = y; // stop TWI - TWI_Stop(); + TWI_Stop (); // success return SSD1306_SUCCESS; @@ -551,19 +580,18 @@ char SSD1306_SetPosition (char x, char y) /** * @desc SSD1306 Check Text Poisition * - * @param char - * @param char + * @param uint8_t address * - * @return char + * @return uint8_t */ -char SSD1306_UpdTxtPosition (void) +uint8_t SSD1306_UpdTxtPosition (uint8_t address) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // check end column position - unsigned short int x = indexCol+CHARS_COLS_LENGTH+1; + unsigned short int x = indexCol + CHARS_COLS_LENGTH + 1; // check position - if ((x > END_COLUMN_ADDR) && (indexPage > (END_PAGE_ADDR-1))) { + if ((x > END_COLUMN_ADDR) && (indexPage > (END_PAGE_ADDR - 1))) { // return out of range return SSD1306_ERROR; // if x out reach end but page in range @@ -571,9 +599,9 @@ char SSD1306_UpdTxtPosition (void) // update - column indexCol = 0; // update - page - indexPage = indexPage+1; + indexPage = indexPage + 1; // update - null col, increment page - status = SSD1306_SetPosition(indexCol, indexPage); + status = SSD1306_SetPosition (address, indexCol, indexPage); // request if (SSD1306_SUCCESS != status) { // error @@ -584,92 +612,22 @@ char SSD1306_UpdTxtPosition (void) return SSD1306_SUCCESS; } -/** - * @desc Draw character - * - * @param char - * - * @return void - */ -char SSD1306_DrawChar (char character) -{ - // variables - uint8_t idxCol = 0; - // init status - char status = INIT_STATUS; - - // update text position - if (SSD1306_UpdTxtPosition() != SSD1306_SUCCESS) { - // error - return SSD1306_ERROR; - } - - // TWI: start & SLAW - // ------------------------- - status = SSD1306_Send_StartAndSLAW(); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // control byte data stream - // ------------------------- - status = TWI_MT_Send_Data(SSD1306_DATA_STREAM); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // loop through 5 bits - while (idxCol < CHARS_COLS_LENGTH) { - // send control byte data - // ------------------------- - status = TWI_MT_Send_Data(pgm_read_byte(&FONTS[character-32][idxCol])); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - // increment - idxCol++; - } - - // empty column - // ------------------------- - status = TWI_MT_Send_Data(CLEAR_COLOR); - // request - start TWI - if (SSD1306_SUCCESS != status) { - // error - return status; - } - - // increment global index col - indexCol = indexCol + CHARS_COLS_LENGTH + 1; - - // stop TWI - TWI_Stop(); - - // success - return SSD1306_SUCCESS; -} - /** * @desc Send 1 Byte of data * - * @param char + * @param uint8_t address + * @param uint8_t data * * @return void */ -char SSD1306_SendByte (char data) +uint8_t SSD1306_SendByte (uint8_t address, uint8_t data) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -678,16 +636,16 @@ char SSD1306_SendByte (char data) // control byte data stream // ------------------------- - status = TWI_MT_Send_Data(SSD1306_DATA_STREAM); - // request - start TWI + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); + // request success if (SSD1306_SUCCESS != status) { // error return status; } // send byte of data // ------------------------- - status = TWI_MT_Send_Data(data); - // request - start TWI + status = TWI_MT_Send_Data (data); + // request success if (SSD1306_SUCCESS != status) { // error return status; @@ -697,30 +655,31 @@ char SSD1306_SendByte (char data) indexCol = indexCol + 1; // stop TWI - TWI_Stop(); + TWI_Stop (); // success return SSD1306_SUCCESS; } /** - * @desc Send Same Bytes + * @desc Send same bytes * - * @param char - * @param char + * @param uint8_t address + * @param uint8_t data + * @param uint8_t length * * @return void */ -char SSD1306_SendBytes (char data, char length) +uint8_t SSD1306_SendSameBytes (uint8_t address, uint8_t data, uint8_t length) { // index unsigned short int i = 0; // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // TWI: start & SLAW // ------------------------- - status = SSD1306_Send_StartAndSLAW(); + status = SSD1306_Send_StartAndSLAW (address); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -729,7 +688,7 @@ char SSD1306_SendBytes (char data, char length) // control byte data stream // ------------------------- - status = TWI_MT_Send_Data(SSD1306_DATA_STREAM); + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -741,7 +700,7 @@ char SSD1306_SendBytes (char data, char length) if (indexCol < MAX_X) { // send byte of data // ------------------------- - status = TWI_MT_Send_Data(data); + status = TWI_MT_Send_Data (data); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -756,7 +715,79 @@ char SSD1306_SendBytes (char data, char length) } // stop TWI - TWI_Stop(); + TWI_Stop (); + + // success + return SSD1306_SUCCESS; +} + +/** + * @desc Draw character + * + * @param uint8_t address + * @param char character + * + * @return uint8_t + */ +uint8_t SSD1306_DrawChar (uint8_t address, char character) +{ + // variables + uint8_t idxCol = 0; + // init status + uint8_t status = INIT_STATUS; + + // update text position + if (SSD1306_UpdTxtPosition (address) != SSD1306_SUCCESS) { + // error + return SSD1306_ERROR; + } + + // TWI: start & SLAW + // ------------------------- + status = SSD1306_Send_StartAndSLAW (address); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // control byte data stream + // ------------------------- + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // loop through 5 bits + while (idxCol < CHARS_COLS_LENGTH) { + // send control byte data + // ------------------------- + status = TWI_MT_Send_Data (pgm_read_byte(&FONTS[character-32][idxCol])); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // increment + idxCol++; + } + + // empty column + // ------------------------- + status = TWI_MT_Send_Data (CLEAR_COLOR); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // increment global index col + indexCol = indexCol + CHARS_COLS_LENGTH + 1; + + // stop TWI + TWI_Stop (); // success return SSD1306_SUCCESS; @@ -765,21 +796,22 @@ char SSD1306_SendBytes (char data, char length) /** * @desc SSD1306 Draw String * - * @param char * + * @param uint8_t address + * @param char * string * * @return void */ -char SSD1306_DrawString (char *str) +uint8_t SSD1306_DrawString (uint8_t address, char *str) { // init status - char status = INIT_STATUS; + uint8_t status = INIT_STATUS; // init int i = 0; // loop through character of string while (str[i] != '\0') { // draw string - status = SSD1306_DrawChar(str[i++]); + status = SSD1306_DrawChar (address, str[i++]); // request - start TWI if (SSD1306_SUCCESS != status) { // error @@ -792,33 +824,245 @@ char SSD1306_DrawString (char *str) } /** - * @desc Draw horizontal line + * @desc Draw pixel * - * @param char - * @param char - * @param char + * @param uint8_t + * @param uint8_t + * @param uint8_t * * @return void */ -char SSD1306_DrawLineHorizontal (char x, char y, char len) +uint8_t SSD1306_DrawPixel (uint8_t address, uint8_t x, uint8_t y) { - char page = 0; - char pixel = 0; + // variables + uint8_t status = INIT_STATUS; + uint8_t page = 0; + uint8_t pixel = 0; // if out of range if ((x > MAX_X) && (y > MAX_Y)) { // out of range return SSD1306_ERROR; } - // find page - page = y / 8; - // which pixel - pixel |= 1 << (y % 8); + // find page (y / 8) + page = y >> 3; + // which pixel (y % 8) + pixel = 1 << (y - (page << 3)); + + // TWI: start & SLAW + // ------------------------- + status = SSD1306_Send_StartAndSLAW (address); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // SET COLUMN address + // *************************************************** + // set column addr + // ------------------------- + status = SSD1306_Send_Command (SSD1306_SET_COLUMN_ADDR); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // start COLUMN + // ------------------------- + status = SSD1306_Send_Command (x); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // end COLUMN + // ------------------------- + status = SSD1306_Send_Command (END_COLUMN_ADDR); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // update column index + indexCol = x; + + // SET PAGE address + // *************************************************** + // set page addr + // ------------------------- + status = SSD1306_Send_Command (SSD1306_SET_PAGE_ADDR); + // request - start TWI + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // start PAGE + // ------------------------- + status = SSD1306_Send_Command (page); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // end PAGE + // ------------------------- + status = SSD1306_Send_Command (END_PAGE_ADDR); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // update column index + indexPage = y; + + // ------------------------------------------------------ + // control byte data stream + status = TWI_MT_Send_Data (SSD1306_DATA_STREAM); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + // send byte of data + status = TWI_MT_Send_Data (pixel); + // request success + if (SSD1306_SUCCESS != status) { + // error + return status; + } + + // increment global index col + indexCol = indexCol + 1; + // ------------------------------------------------------ + + // stop TWI + TWI_Stop (); + + // success + return SSD1306_SUCCESS; +} + +/** + * @desc Draw line by Bresenham algoritm + * @surce https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm + * + * @param uint8_t address + * @param uint8_t x start position / 0 <= cols <= MAX_X-1 + * @param uint8_t x end position / 0 <= cols <= MAX_X-1 + * @param uint8_t y start position / 0 <= rows <= MAX_Y-1 + * @param uint8_t y end position / 0 <= rows <= MAX_Y-1 + * + * @return uint8_t + */ +uint8_t SSD1306_DrawLine (uint8_t address, uint8_t x1, uint8_t x2, uint8_t y1, uint8_t y2) +{ + // determinant + int16_t D; + // deltas + int16_t delta_x, delta_y; + // steps + int16_t trace_x = 1, trace_y = 1; + + // delta x + delta_x = x2 - x1; + // delta y + delta_y = y2 - y1; + + // check if x2 > x1 + if (delta_x < 0) { + // negate delta x + delta_x = -delta_x; + // negate step x + trace_x = -trace_x; + } + + // check if y2 > y1 + if (delta_y < 0) { + // negate detla y + delta_y = -delta_y; + // negate step y + trace_y = -trace_y; + } + + // Bresenham condition for m < 1 (dy < dx) + if (delta_y < delta_x) { + // calculate determinant + D = (delta_y << 1) - delta_x; + // draw first pixel + SSD1306_DrawPixel (address, x1, y1); + // check if x1 equal x2 + while (x1 != x2) { + // update x1 + x1 += trace_x; + // check if determinant is positive + if (D >= 0) { + // update y1 + y1 += trace_y; + // update determinant + D -= 2*delta_x; + } + // update deteminant + D += 2*delta_y; + // draw next pixel + SSD1306_DrawPixel (address, x1, y1); + } + // for m > 1 (dy > dx) + } else { + // calculate determinant + D = delta_y - (delta_x << 1); + // draw first pixel + SSD1306_DrawPixel (address, x1, y1); + // check if y2 equal y1 + while (y1 != y2) { + // update y1 + y1 += trace_y; + // check if determinant is positive + if (D <= 0) { + // update y1 + x1 += trace_x; + // update determinant + D += 2*delta_y; + } + // update deteminant + D -= 2*delta_x; + // draw next pixel + SSD1306_DrawPixel (address, x1, y1); + } + } + // success return + return SSD1306_SUCCESS; +} + +/** + * @desc Draw horizontal line + * + * @param uint8_t address + * @param uint8_t + * @param uint8_t + * @param uint8_t + * + * @return void + */ +uint8_t SSD1306_DrawLineHorizontal (uint8_t address, uint8_t x, uint8_t y, uint8_t len) +{ + uint8_t page = 0; + uint8_t pixel = 0; + + // if out of range + if ((x > MAX_X) && (y > MAX_Y)) { + // out of range + return SSD1306_ERROR; + } + // find page (y / 8) + page = y >> 3; + // which pixel (y % 8) + pixel |= 1 << (y - (page << 3)); // send position - SSD1306_SetPosition(x, page); + SSD1306_SetPosition (address, x, page); // draw pixel - SSD1306_SendBytes(pixel, len); + SSD1306_SendSameBytes (address, pixel, len); // success return SSD1306_SUCCESS; diff --git a/lib/ssd1306.h b/lib/ssd1306.h index bafd300..58dfb5c 100644 --- a/lib/ssd1306.h +++ b/lib/ssd1306.h @@ -7,10 +7,14 @@ * * @author Marian Hrinko * @datum 06.10.2020 + * @update 19.07.2021 * @file ssd1306.h - * @tested AVR Atmega16, ATmega8, Atmega328 + * @tested AVR Atmega328 * - * @depend + * @depend font.h, twi.h + * ---------------------------------------------------------------+ + * @descr Version 1.0 -> applicable for 1 display + * Version 2.0 -> applicable for more than 1 display * ---------------------------------------------------------------+ * @usage Basic Setup for OLED Display */ @@ -20,15 +24,11 @@ // Success // ------------------------------------------- - #ifndef SSD1306_SUCCESS - #define SSD1306_SUCCESS 0 - #endif + #define SSD1306_SUCCESS 0 // Error // ------------------------------------------- - #ifndef SSD1306_ERROR - #define SSD1306_ERROR 1 - #endif + #define SSD1306_ERROR 1 // Address definition // ----------------------------------- @@ -81,6 +81,8 @@ #define START_COLUMN_ADDR 0 #define END_COLUMN_ADDR 127 + #define CACHE_SIZE_MEM (1 + END_PAGE_ADDR) * (1 + END_COLUMN_ADDR) + #define MAX_X END_COLUMN_ADDR #define MAX_Y (END_PAGE_ADDR+1)*8 @@ -94,140 +96,170 @@ /** * @desc SSD1306 Init - set frequency * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_Init (void); + uint8_t SSD1306_Init (uint8_t); /** * @desc SSD1306 Send Start and SLAW request * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_Send_StartAndSLAW (void); + uint8_t SSD1306_Send_StartAndSLAW (uint8_t); /** * @desc SSD1306 Send Command * - * @param char + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_Send_Command (char); + uint8_t SSD1306_Send_Command (uint8_t); /** * @desc SSD1306 Clear Screen * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_ClearScreen (void); + uint8_t SSD1306_ClearScreen (uint8_t); /** * @desc SSD1306 Normal Colors * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_NormalScreen (void); + uint8_t SSD1306_NormalScreen (uint8_t); /** * @desc SSD1306 Inverse Colors * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_InverseScreen (void); + uint8_t SSD1306_InverseScreen (uint8_t); /** * @desc SSD1306 Check Text Position * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_UpdTxtPosition (void); + uint8_t SSD1306_UpdTxtPosition (uint8_t); /** * @desc SSD1306 Set Position * - * @param char -> column - * @param char -> page + * @param uint8_t + * @param uint8_t + * @param uint8_t * * @return char */ - char SSD1306_SetPosition (char, char); + uint8_t SSD1306_SetPosition (uint8_t, uint8_t, uint8_t); /** * @desc SSD1306 Draw Character * + * @param uint8_t * @param char * - * @return char + * @return uint8_t */ - char SSD1306_DrawChar (char); + uint8_t SSD1306_DrawChar (uint8_t, char); /** * @desc SSD1306 Draw String * + * @param uint8_t * @param char * * - * @return char + * @return uint8_t */ - char SSD1306_DrawString (char *); + uint8_t SSD1306_DrawString (uint8_t, char *); /** * @desc SSD1306 Update Screen On * - * @param void + * @param uint8_t * - * @return char + * @return uint8_t */ - char SSD1306_UpdateScreen (void); + uint8_t SSD1306_UpdateScreen (uint8_t); /** * @desc SSD1306 Update indexes * - * @param char + * @param uint8_t * * @return void */ - void SSD1306_UpdateIndexes (char); + void SSD1306_UpdateIndexes (uint8_t); + + /** + * @desc Draw pixel + * + * @param uint8_t + * @param uint8_t + * @param uint8_t + * + * @return void + */ + uint8_t SSD1306_DrawPixel (uint8_t, uint8_t, uint8_t); + + /** + * @desc Draw line + * + * @param uint8_t + * @param uint8_t + * @param uint8_t + * @param uint8_t + * @param uint8_t + * + * @return uint8_t + */ + uint8_t SSD1306_DrawLine (uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); /** * @desc SSD1306 Draw line horizontal * - * @param char - * @param char - * @param char + * @param uint8_t + * @param uint8_t + * @param uint8_t + * @param uint8_t * * @return void */ - char SSD1306_DrawLineHorizontal (char, char, char); + uint8_t SSD1306_DrawLineHorizontal (uint8_t, uint8_t, uint8_t, uint8_t); /** - * @desc SSD1306 Send Byte + * @desc SSD1306 Send byte * - * @param char + * @param uint8_t + * @param uint8_t * * @return void */ - char SSD1306_SendByte (char); + uint8_t SSD1306_SendByte (uint8_t, uint8_t); /** - * @desc SSD1306 Send Bytes + * @desc SSD1306 Send same bytes * - * @param char - * @param char + * @param uint8_t + * @param uint8_t + * @param uint8_t * - * @return void + * @return uint8_t */ - char SSD1306_SendBytes (char, char); + uint8_t SSD1306_SendSameBytes (uint8_t, uint8_t, uint8_t); #endif diff --git a/lib/twi.c b/lib/twi.c index 4ef2fb0..da01dc1 100644 --- a/lib/twi.c +++ b/lib/twi.c @@ -26,7 +26,7 @@ * * @return void */ -void TWI_Init(void) +void TWI_Init (void) { // +++++++++++++++++++++++++++++++++++++++++++++ // Calculation fclk: @@ -38,10 +38,10 @@ void TWI_Init(void) // TWBR = {(fcpu/fclk) - 16 } / (2*4^Prescaler) // +++++++++++++++++++++++++++++++++++++++++++++ // @param1 value of TWBR, - // fclk = 400 kHz; TWBR = 3 - // fclk = 100 kHz; TWBR = 20 + // fclk = 400kHz (m16); TWBR = 3 + // fclk = 100kHz (m16); TWBR = 20 // @param2 value of Prescaler = 1 - TWI_FREQ(20, 1); + TWI_FREQ (3, 1); } /** @@ -51,7 +51,7 @@ void TWI_Init(void) * * @return char */ -char TWI_MT_Start(void) +char TWI_MT_Start (void) { // null status flag TWI_TWSR &= ~0xA8; @@ -77,7 +77,7 @@ char TWI_MT_Start(void) * * @return char */ -char TWI_MT_Send_SLAW(char address) +char TWI_MT_Send_SLAW (char address) { // SLA+W // ---------------------------------------------- @@ -103,7 +103,7 @@ char TWI_MT_Send_SLAW(char address) * * @return char */ -char TWI_MT_Send_Data(char data) +char TWI_MT_Send_Data (char data) { // DATA // ---------------------------------------------- @@ -129,7 +129,7 @@ char TWI_MT_Send_Data(char data) * * @return char */ -char TWI_MR_Send_SLAR(char address) +char TWI_MR_Send_SLAR (char address) { // SLA+R // ---------------------------------------------- @@ -155,12 +155,12 @@ char TWI_MR_Send_SLAR(char address) * * @return void */ -void TWI_Stop(void) +void TWI_Stop (void) { // End TWI // ------------------------------------------------- // send stop sequence - TWI_STOP(); + TWI_STOP (); // wait for TWINT flag is set // TWI_WAIT_TILL_TWINT_IS_SET(); } diff --git a/main.c b/main.c index af4932e..b99e3d1 100644 --- a/main.c +++ b/main.c @@ -1,17 +1,21 @@ /** - * -------------------------------------------------------------+ + * ---------------------------------------------------------------+ * @desc OLED SSD1306 example - * -------------------------------------------------------------+ + * ---------------------------------------------------------------+ * Copyright (C) 2020 Marian Hrinko. * Written by Marian Hrinko (mato.hrinko@gmail.com) * * @author Marian Hrinko * @datum 06.10.2020 + * @update 19.07.2021 * @file main.c - * @tested AVR Atmega16 + * @tested AVR Atmega328p * * @depend ssd1306.h - * -------------------------------------------------------------+ + * ---------------------------------------------------------------+ + * @descr Version 1.0 -> applicable for 1 display + * Version 2.0 -> applicable for more than 1 display + * ---------------------------------------------------------------+ */ // include libraries @@ -27,21 +31,35 @@ */ int main(void) { + uint8_t addr = SSD1306_ADDRESS; + // init ssd1306 - SSD1306_Init(); + SSD1306_Init (addr); // clear screen - SSD1306_ClearScreen(); + SSD1306_ClearScreen (addr); // draw line - SSD1306_DrawLineHorizontal(4, 4, 115); + //SSD1306_DrawLineHorizontal (addr, 4, 4, 115); // set position x, y - SSD1306_SetPosition(5, 1); + //SSD1306_SetPosition (addr, 1, 1); + + //for (char i = 1; i < MAX_Y; i++) { + SSD1306_DrawPixel (addr, 0, 0); + SSD1306_DrawPixel (addr, 0, 1); + SSD1306_DrawPixel (addr, 0, 2); + SSD1306_DrawPixel (addr, 1, 1); + SSD1306_DrawPixel (addr, 2, 2); + SSD1306_DrawPixel (addr, 3, 3); + SSD1306_DrawPixel (addr, 4, 4); + //SSD1306_DrawLine (addr, 0, MAX_X-1, MAX_Y-1, MAX_Y-1); + //} + // draw string - SSD1306_DrawString("SSD1306 OLED DRIVER"); + //SSD1306_DrawString (addr, "SSD1306 OLED DRIVER"); // draw line - SSD1306_DrawLineHorizontal(4, 18, 115); + //SSD1306_DrawLineHorizontal (addr, 4, 18, 115); // update - SSD1306_UpdateScreen(); + //SSD1306_UpdateScreen (addr); // return value return 0; diff --git a/main.hex b/main.hex index f75dfb2..f117127 100644 --- a/main.hex +++ b/main.hex @@ -1,112 +1,145 @@ -:100000000C9433010C9450010C9450010C94500149 -:100010000C9450010C9450010C9450010C9450011C -:100020000C9450010C9450010C9450010C9450010C -:100030000C9450010C9450010C9450010C945001FC -:100040000C9450010C9450010C9450010C945001EC -:100050000C9450010000000000818118818100078C -:10006000000700147F147F14242A7F2A1223130808 -:10007000646236495522500005030000001C2241ED -:10008000000041221C0014083E081408083E08081D -:1000900000503000000808080808006060000020D8 -:1000A000100804023E5149453E00427F4000426133 -:1000B0005149462141454B311814127F10274545BF -:1000C00045393C4A494930017109050336494949D6 -:1000D00036064949291E0036360000005636000013 -:1000E00008142241001414141414004122140802AC -:1000F00001510906324979413E7E1111117E7F4935 -:100100004949363E414141227F4141221C7F4949B4 -:1001100049417F090909013E4149497A7F08080898 -:100120007F00417F41002040413F017F0814224170 -:100130007F404040407F020C027F7F0408107F3EDA -:100140004141413E7F090909063E4151215E7F0937 -:10015000192946464949493101017F01013F404083 -:10016000403F1F2040201F3F4038403F6314081489 -:100170006307087008076151494543007F4141000A -:1001800002040810200041417F00040201020440E3 -:1001900040404040000102040020545454787F48FD -:1001A0004444383844444420384444487F38545404 -:1001B0005418087E0901020C5252523E7F08040472 -:1001C0007800447D40002040443D007F10284400DA -:1001D00000417F40007C041804787C0804047838CF -:1001E000444444387C14141408081414147C7C0807 -:1001F0000404084854545420043F4440203C4040E8 -:10020000207C1C2040201C3C4030403C44281028CE -:10021000440C5050503C4464544C44000836410057 -:1002200000007F0000004136080010080810080098 -:10023000000000001200AE01A83F012000022100D2 -:100240007F02220007004001D30000A100C801DAAC -:100250001201817F00A400A601D58001D9C201DB73 -:1002600020018D1400AF11241FBECFE5D4E0DEBF06 -:10027000CDBF10E0A0E6B0E0ECECF6E002C00590E7 -:100280000D92A637B107D9F720E0A6E7B0E001C08C -:100290001D92AA37B207E1F70E9449030C94640348 -:1002A0000C94000084E180B981B191B182708160C9 -:1002B000892B81B9089581B1877581B984EA86BF98 -:1002C00006B607FEFDCF81B1887F883039F081B155 -:1002D000887F803119F081B1887F089580E008958A -:1002E000880F83B984E886BF06B607FEFDCF81B1CB -:1002F000887F883119F081B1887F089580E0089562 -:1003000083B984E886BF06B607FEFDCF81B1887F3A -:10031000883219F081B1887F089580E00895880FB0 -:10032000990B816083B984E886BF06B607FEFDCFCE -:1003300081B1887F803419F081B1887F089580E091 -:10034000089584E986BF08950E945B01811103C06E -:100350008CE30C9470010895CF93C82F80E80E941D -:100360008001811104C08C2FCF910C948001CF911A -:100370000895FF920F931F93CF93DF93E4E3F2E08E -:10038000C4910E9452010E94A40181111FC0D0E0BB -:10039000E5E3F2E02097B9F0F4908F010E5F1F4F74 -:1003A000319684910E94AC01811110C0F801FF20A8 -:1003B00041F0F80184910E94AC01FA940F5F1F4F45 -:1003C000F3CF2197E7CF0E94A10180E0DF91CF9189 -:1003D0001F910F91FF9008950E94A401811103C005 -:1003E00086EA0C94AC0108950E94A401811103C017 -:1003F00087EA0C94AC0108950E94A401811103C006 -:100400008FEA0C94AC010895CF93DF93D82FC62FB9 -:100410000E94A40181112FC081E20E94AC018111D0 -:100420002AC08D2F0E94AC01811125C08FE70E9448 -:10043000AC01811120C08D2FDD0F990B90937900B5 -:100440008093780082E20E94AC01811114C08C2F4D -:100450000E94AC0181110FC087E00E94AC018111A4 -:100460000AC06C2FCC0F770B7093770060937600E7 -:100470000E94A10180E0DF91CF910895CF93DF9397 -:1004800060E080E00E94040281111CC00E94A4016F -:10049000811118C080E40E948001811113C0C0E066 -:1004A000D0E08091600090916100C817D90738F4BE -:1004B00080E00E948001811105C02196F2CF0E9448 -:1004C000A10180E0DF91CF91089580917800909113 -:1004D0007900069680389105A0F0609176007091C1 -:1004E00077006730710578F41092790010927800E7 -:1004F0006F5F7F4F709377006093760080E00C947D -:10050000040280E0089581E008950F931F93CF9334 -:10051000DF93C82F0E946502811131C00E94A4019F -:1005200081112EC080E40E948001811129C085E0E4 -:10053000C802E001112400E010E0C05AD109FE0118 -:10054000E00FF11FEC5AFF4F84910E94800181114E -:1005500017C00F5F1F4F0530110589F70E948001FA -:1005600081110EC080917800909179000696909349 -:100570007900809378000E94A10180E001C081E0B1 -:10058000DF91CF911F910F910895CF93C82F0E94B3 -:10059000A401811116C080E40E948001811111C064 -:1005A0008C2F0E94800181110CC080917800909165 -:1005B0007900019690937900809378000E94A101C0 -:1005C00080E0CF910895FF920F931F93CF93DF9315 -:1005D000F82EC62F0E94A401811128C080E40E9439 -:1005E0008001811123C000E010E00C2E000CDD0B17 -:1005F0000C171D07C0F480917800909179008F3717 -:10060000910588F48F2D0E9480010F5F1F4F81118B -:100610000DC08091780090917900019690937900B7 -:1006200080937800E5CF0E94A10180E0DF91CF9117 -:100630001F910F91FF900895CF93DF93EC01899163 -:10064000882321F00E9485028823C9F3DF91CF918E -:100650000895CF93DF93C62FD42F67FF02C067E0C2 -:100660006C0F6595659565950E9404029C2F97789F -:1006700097FF03C09150986F9F5F6D2F81E001C07D -:10068000880F9A95EAF70E94E30280E0DF91CF910C -:1006900008950E94B9010E943E0243E764E084E0AD -:1006A0000E94290361E085E00E94040282E690E056 -:1006B0000E941C0343E762E184E00E9429030E9438 -:0C06C000FC0180E090E00895F894FFCF6A -:1006CC00000453534431333036204F4C45442044BE -:0606DC0052495645520090 +:100000000C943D010C945A010C945A010C945A0121 +:100010000C945A010C945A010C945A010C945A01F4 +:100020000C945A010C945A010C945A010C945A01E4 +:100030000C945A010C945A010C945A010C945A01D4 +:100040000C945A010C945A010C945A010C945A01C4 +:100050000C945A010C945A010C945A010C945A01B4 +:100060000C945A010C945A01000000000081811880 +:1000700081810007000700147F147F14242A7F2A3F +:10008000122313086462364955225000050300000C +:10009000001C2241000041221C0014083E081408E4 +:1000A000083E0808005030000008080808080060F2 +:1000B00060000020100804023E5149453E00427F86 +:1000C000400042615149462141454B311814127F8D +:1000D0001027454545393C4A494930017109050316 +:1000E0003649494936064949291E0036360000007E +:1000F0005636000008142241001414141414004150 +:100100002214080201510906324979413E7E11113B +:10011000117E7F494949363E414141227F4141227A +:100120001C7F494949417F090909013E4149497AF2 +:100130007F0808087F00417F41002040413F017F48 +:10014000081422417F404040407F020C027F7F0420 +:1001500008107F3E4141413E7F090909063E415159 +:10016000215E7F09192946464949493101017F012C +:10017000013F4040403F1F2040201F3F4038403F4C +:100180006314081463070870080761514945430068 +:100190007F41410002040810200041417F00040219 +:1001A0000102044040404040000102040020545439 +:1001B00054787F48444438384444442038444448C0 +:1001C0007F3854545418087E0901020C5252523E92 +:1001D0007F0804047800447D40002040443D007FB7 +:1001E0001028440000417F40007C041804787C08FB +:1001F00004047838444444387C1414140808141453 +:10020000147C7C080404084854545420043F44409F +:10021000203C4040207C1C2040201C3C4030403C86 +:1002200044281028440C5050503C4464544C440022 +:100230000836410000007F00000041360800100829 +:1002400008100800000000001200AE01A83F0120C5 +:10025000000221007F02220007004001D30000A11C +:1002600000C801DA1201817F00A400A601D5800137 +:10027000D9C201DB20018D1400AF11241FBECFEFC6 +:10028000D8E0DEBFCDBF11E0A0E0B1E0E6EEF8E0DF +:1002900002C005900D92A230B107D9F725E0A2E087 +:1002A000B1E001C01D92A630B207E1F70E944504FB +:1002B0000C9471040C94000083E08093B800E9EB87 +:1002C000F0E08081908182708160892B8083089525 +:1002D0008091B90087758093B90084EA8093BC004F +:1002E0008091BC0087FFFCCF8091B900887F883067 +:1002F00049F08091B900887F803121F08091B90068 +:10030000887F089580E00895880F8093BB0084E87B +:100310008093BC008091BC0087FFFCCF8091B90026 +:10032000887F883121F08091B900887F089580E02E +:1003300008958093BB0084E88093BC008091BC004A +:1003400087FFFCCF8091B900887F883221F08091AF +:10035000B900887F089580E00895880F990B816027 +:100360008093BB0084E88093BC008091BC0087FF31 +:10037000FCCF8091B900887F803421F08091B90052 +:10038000887F089580E0089584E98093BC000895F3 +:10039000CF93C82F0E946801811104C08C2FCF9188 +:1003A0000C948401CF910895CF93C82F80E80E94C8 +:1003B0009901811104C08C2FCF910C949901CF9198 +:1003C0000895FF920F931F93CF93DF93D82FE8E404 +:1003D000F2E0C4910E945C018D2F0E94C80181113E +:1003E0001FC0D0E0E9E4F2E02097B9F0F4908F016B +:1003F0000E5F1F4F319684910E94D401811110C06D +:10040000F801FF2041F0F80184910E94D401FA9490 +:100410000F5F1F4FF3CF2197E7CF0E94C40180E009 +:10042000DF91CF911F910F91FF9008950E94C80115 +:10043000811103C086EA0C94D40108950E94C8017A +:10044000811103C087EA0C94D4010895CF93DF9300 +:100450000E94C801811113C080E40E94990181119A +:100460000EC0C6E0D1E089910E949901811107C0B8 +:1004700085E0C630D807B9F70E94C40180E0DF915B +:10048000CF91089580E094E0E6E0F1E0DF019C0187 +:100490001D9221503040E1F780E00895CF93DF9323 +:1004A000D62FC42F0E94C80181112DC081E20E9465 +:1004B000D401811128C08D2F0E94D401811123C045 +:1004C0008FE70E94D40181111EC06D2F70E07093E0 +:1004D00005016093040182E20E94D401811113C0DE +:1004E0008C2F0E94D40181110EC087E00E94D4019C +:1004F000811109C04C2F50E0509303014093020139 +:100500000E94C40180E0DF91CF9108952091040101 +:10051000309105012A5F3F4F20383105A0F040910E +:100520000201509103014730510578F41092050102 +:10053000109204014F5F5F4F5093030140930201FB +:1005400060E00C944E0280E0089581E00895CF931E +:10055000C62F0E94C801811116C080E40E94990133 +:10056000811111C08C2F0E94990181110CC08091C2 +:100570000401909105010196909305018093040177 +:100580000E94C40180E0CF910895FF920F931F93C2 +:10059000CF93DF93F62EC42F0E94C801811126C08D +:1005A00080E40E949901811121C000E010E0D0E0B8 +:1005B0000C171D07C0F480910401909105018F373D +:1005C000910588F48F2D0E9499010F5F1F4F8111B3 +:1005D0000DC0809104019091050101969093050151 +:1005E00080930401E5CF0E94C40180E0DF91CF91A8 +:1005F0001F910F91FF9008950F931F93CF93DF9357 +:10060000D82FC62F0E948602811132C08D2F0E94E2 +:10061000C80181112EC080E40E949901811129C076 +:1006200085E0C802E001112400E010E0C05AD109C1 +:10063000FE01E00FF11FE859FF4F84910E949901DC +:10064000811117C00F5F1F4F0530110589F70E94F8 +:10065000990181110EC080910401909105010696C7 +:1006600090930501809304010E94C40180E001C0C1 +:1006700081E0DF91CF911F910F9108951F93CF9348 +:10068000DF93182FEB016991662331F0812F0E94CF +:10069000FC028823C1F301C080E0DF91CF911F915C +:1006A00008951F93CF93DF9367FF03C0413408F091 +:1006B00053C0C42FD62F0E94C80181114EC081E2C1 +:1006C0000E94D401811149C08D2F0E94D401811153 +:1006D00044C08FE70E94D40181113FC06D2F70E0AC +:1006E000709305016093040182E20E94D40181119C +:1006F00034C01C2F169516951695812F0E94D40193 +:1007000081112BC087E00E94D401811126C0D0E066 +:10071000D0930301C093020180E40E9499018111EA +:100720001CC088E0189FC019D109112481E001C0C4 +:10073000880FCA95EAF70E94990181110EC0809135 +:1007400004019091050101969093050180930401A5 +:100750000E94C40180E001C081E0DF91CF911F9130 +:1007600008957F928F929F92AF92BF92CF92DF9225 +:10077000EF92FF920F931F93CF93DF93E82EF62E05 +:10078000742E122F802E242F30E0261B3109802F4B +:1007900090E0811B910937FF05C031952195310902 +:1007A0000FEF01C001E097FF06C091958195910977 +:1007B00099249A9402C0992493946C01CC0CDD1C6A +:1007C0005901AA0CBB1C821793079CF4E601C21BBB +:1007D000D30B412F6F2D8E2D0E945103F714D9F0AA +:1007E000F00ED7FD03C0190DCA19DB09CC0DDD1DB4 +:1007F000F0CFEC01CA19DB09412F6F2D8E2D0E941D +:100800005103181541F0190D1C161D069CF3F00E2E +:10081000CC0DDD1DEFCF80E0DF91CF911F910F91C7 +:10082000FF90EF90DF90CF90BF90AF909F908F9010 +:100830007F9008950F931F93CF93DF9367FF02C0BC +:100840004134E8F4122FC42FD82F042F06950695B3 +:100850000695402F0E944E024C2F50E088E0089FE2 +:1008600040195109112461E070E001C0660F4A95FA +:10087000EAF7412F8D2F0E94C50280E001C081E080 +:10088000DF91CF911F910F9108958CE30E94E101B8 +:100890008CE30E94420240E060E08CE30E9451033E +:1008A00041E060E08CE30E94510342E060E08CE3B1 +:1008B0000E94510341E061E08CE30E94510342E059 +:1008C00062E08CE30E94510343E063E08CE30E940A +:1008D000510344E064E08CE30E94510380E090E027 +:0608E0000895F894FFCF1B +:0208E60000040C :00000001FF