SSD1306 + AtMega8(88) = проблемы отображения

Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить
Опытный кот
Сообщения: 768
Зарегистрирован: Вс янв 19, 2014 00:55:09

Сообщение MOHCTEP »

Здоровья котам! :beer:
Друзья, помогите пожалуйста убить проблему. Несколько вечеров возюкаюсь безрезультатно. То ли уже не вижу ничего, то ли она в принципе не решаема?
Изображение
На фото, слева - у-координаты символов кратны 8 и здесь все в порядке и с картинкой (вверху) и со строкой ("768"). А вот справа, у-координаты картинки = 4, текста = 35 и снизу вылезла непонятная "грязь".
Вывод посимвольный, буфер(512 байт), всякий раз чистится и заполняется побитово. Здесь псевдокод ответственный за проблему.
Спойлер

Код: Выделить всё

/*
 * temp_ssd1306.c
 *
 * Created: 18.02.2020 22:36:34
 *  Author: МОНСТЕР
 */
 
#define  BUFFER_LENGTH	SSD1306_WIDTH * SSD1306_HEIGHT >>4
#define  BUFFER_ROWS	BUFFER_LENGTH / SSD1306_WIDTH * 8

uint8_t dev_address;
uint8_t _oled_buffer[BUFFER_LENGTH]; /*512 bytes*/

/*
эта функция меняет состояние соответствующего бита
в распределенных элементах буфера, определяемого координатами х/у.
По идее, биты полученные от единого байта из draw_byte(), здесь могут распределиться по разным байтам буфера.
*/
void draw_pixel(int16_t x, int16_t y, uint16_t color, uint8_t width){
	//y %= BUFFER_ROWS;
	uint16_t  bi = width * (y / 8) + x;
	color?(_oled_buffer[bi] |= (1 << (y & 0x07))):(_oled_buffer[bi] &= ~(1 << (y & 0x07)));
}

/*
Состояние каждого бита аргумента byte поочередно отправляется для модификации буфера.
Остальные аргументы - сквозняком.
*/
void draw_byte(uint8_t byte, int16_t x, int16_t y, uint8_t width){
	uint8_t bit = 8;
	while(bit--){
		draw_pixel(x, y, byte & 0x80, width);
		byte <<= 1;
		y++;
	}
}

/*
В памяти дисплея задаем прямоугольную область для дальнейшей отрисовки.
Возвращается байтовый размер этой области.
*/
uint16_t set_area(int x, int y, uint8_t width, uint8_t height){
	uint8_t page_0 = y >> 3;							//стартовая страница области
	uint8_t pages = height >> 3;						//общее количество
	while((height + (y & 0x07)) > (pages << 3)) pages++;//страниц области
	i2c_cmd(0x21);										//установка столбца
	i2c_cmd(x);											//Начальный столбец.
	i2c_cmd(x + width - 1);								//Конечный столбец.
	i2c_cmd(0x22);										//установка страницы
	i2c_cmd(page_0);									//Начальная страница.
	i2c_cmd(page_0 + pages - 1);						//Конечная страница.
	return (pages * width);								//Байтовый размер области
}

/*
Вывод символа в заданные координаты экрана.
\	char c - код символа
\	int x, int y - пиксельные координаты лево-верхнего пиксела символа.
cfont - структура моноширинного шрифта. Здесь:
\	cfont.data - массив шрифта + служебные байты. Сам шрифт смещен на величину FONT_DATA_SHIFT от начала массива.
\	cfont.x_size, cfont.y_size - пиксельные размеры символа шрифта.
\	cfont.bytes - количество байт на символ в массиве шрифта.
\	cfont.interspace - межсимвольный промежуток в слове.

*/
void _print_char(char c, int x, int y){
	uint16_t size, sym_id;		//байт-размер области, смещение и счетчик байт символа в массиве шрифта.
	uint8_t pages, current_y;	//вертикальный размер символа в страницах дисплея, смещение по "у" для обрабатываемой страницы.
	size = set_area(x, y, cfont.x_size + cfont.interspace, cfont.y_size);
	memset(_oled_buffer, 0, size);
	current_y = y & 7;  pages = 1;
	while((current_y + cfont.y_size) > (pages << 3)) pages++;
	//декодирование символа c
	switch (c){
		case 0x2a: sym_id = FONT_DATA_SHIFT; break; // "*"
		default: sym_id = (c - 0x2c) * cfont.bytes + FONT_DATA_SHIFT; // "-./0123456789:"
	}
	//******************
	for (uint8_t dy = 0; dy < pages; dy++){
		for (uint8_t dx = 0; dx < cfont.x_size; dx++){
			draw_byte(pgm_read_byte(&cfont.data[sym_id++]), dx, current_y, cfont.x_size + cfont.interspace);
		}
		current_y += 8;
	}
	i2c_data_array(_oled_buffer, size);
}

/*	Вывод строки	*/
void oled_print(char *st, int x, int y){
	while(*st != '\0'){
		_print_char(*st++, x, y);
		x += (cfont.x_size + cfont.interspace);
	}
}

/*
Вывод картинки. 
Алгоритм во многом повторяет процедуру _print_char.
*/
void oled_drawImage(const uint8_t* image, int x, int y){
	cimage.data = image;
	cimage.width = pgm_read_byte(&cimage.data[OLED_IMG_WIDTH]);
	cimage.height = pgm_read_byte(&cimage.data[OLED_IMG_HEIGHT]);
	cimage.bytes = pgm_read_byte(&cimage.data[OLED_IMG_BYTES]);
	uint16_t size = set_area(x, y, cimage.width, cimage.height);
	uint16_t shift = OLED_IMG_SHIFT;
	uint8_t sy = y & 7, pages = 1;
	memset(_oled_buffer, 0, size);
	//memset(_oled_buffer, 0, BUFFER_LENGTH);
	while((sy + cimage.height) > (pages << 3)) pages++;
	for (uint8_t dy = 0; dy < pages; dy++){
		for (uint8_t dx = 0; dx < cimage.width; dx++){
			draw_byte(pgm_read_byte(&cimage.data[shift++]), dx, sy, cimage.width);
		}
		sy += 8;
	}
	i2c_data_array(_oled_buffer, size);
}
Реклама
Мудрый кот
Сообщения: 1808
Зарегистрирован: Пт мар 26, 2010 17:16:52
Откуда: Kazan

Сообщение pcb432 »

Может у вас SH1106?
Присылают под видом SSD1306/
Вот пишут
Контроллер оказался проще, чем ssd1306. В частности, он не поддерживает режимы горизонтальной и вертикальной адресации, а умеет только страничную адресацию. Возможности по ремапу строк столбцов тоже ограничены. Поэтому попытка запуска экранчика с кодом от 1306 привела к закономерной неудаче.
Реклама
Опытный кот
Сообщения: 768
Зарегистрирован: Вс янв 19, 2014 00:55:09

Сообщение MOHCTEP »

pcb432, Здесь горизонтальная адресация и она работает вроде... Картинка 16х16, а шрифт 10х14 бит. Т.е. занимают 2, а когда и 3 страницы (это если координата у не кратна 8). Так вот 2 страницы рисуются идеально,
при любых координатах, а на 3 почему-то мусор.
Опытный кот
Сообщения: 768
Зарегистрирован: Вс янв 19, 2014 00:55:09

Сообщение MOHCTEP »

Решил задачу. Видимо передоз программирования был. :)
В процедурах _print_char(...) и oled_drawImage(...) битовое смещение, в циклах подсчета страниц, было лишним. Правильно работающие строчки таковы:

Код: Выделить всё

void _print_char(char c, int x, int y){
   ...
   while(cfont.y_size > (pages << 3)) pages++;
  ...
}

void oled_drawImage(const uint8_t* image, int x, int y){
   ...
   while(cimage.height > (pages << 3)) pages++;
   ...
}
Реклама
Эиком - электронные компоненты и радиодетали
Ответить

Вернуться в «Периферия»