Библиотека на СИ, для LCD дисплеев на базе процессора HD44780, для микроконтроллеров AVR

AVR
Время от времени мне приходится использовать LCD дисплеи в разных изделиях. Эти изделия базируются на AVR микроконтроллерах. Раньше я писал проги на асме, и у меня была написанная мною библиотечка для этих дисплеев. Но недавно пересел на СИ. И написанной мной библиотеки на СИ еще не было. Поэтому пользовался чужими, скачанными откуда то и когда то. Но вот решил замутить для себя свою собственную сишную библиотеку. Одна из целей данной библиотеки, это лучше научится программировать на СИ. Ведь научиться можно чему то, лишь делая что то. На идеальность и красоту кода не претендую, так как я еще учусь. Если увидите, что можно улучшить, пишите. Интересно будет посмотреть.

Итак, что может данная библиотека? Поддерживается 4х и 8ми битное подключение. Поддерживается только вывод символов на дисплей. Поддержки работы с памятью CGRAM нету. Ибо за все время использования этих дисплеев, мне это ни разу не требовалось. Кажется, что и не потребуется.

Прежде чем начать что то писать, решил сперва освежить в памяти принцип работы с этими дисплеями. Для этого было нарыто 2 мануала на русском. Обратил внимание на несколько пунктов, которые мне показались важными.

Первый пункт — правильная инициализация дисплея.
Нужно выдержать паузу не менее 15 мс между установлением рабочего напряжения питания >4.5 В и выполнением каких либо операций с дисплеем. Три раза подряд подать команду 0x30 на шину LCD без проверки флага занятости BF. Между каждой командой нужно выдерживать временные паузы не менее 4.1 мс и 100 мкс.

Значит инициализация будет такой:
1.Включаем питание.
2.После VCC >=4.5V ждем не менее 15 мсек.
3.Отправка команды 0x30 — 0b110000
4.Ждем не менее 4.1 мсек
5.Отправка команды 0x30
6.Ждем не менее 100 мксек
7.Отправка команды 0x30
Приведенные выше операции являются инициализирующими для LCD
и способны вывести дисплей в рабочий режим из любого состояния.
8.Далее работаем с дисплеем в обычном режиме.

Второй пункт — назначать правильное состояние портам микроконтроллера.
В исходном состоянии LCD, Е=0, R/W=0, RS-произвольно. Шина данных DB0-DB7 в состоянии высокого импеданса (HI). Такое состояние должно поддерживаться все время в промежутках между операциями обмена с LCD.

Третий пункт — период сигнала Е, должен быть не менее 500 нс. Большинство операций выполняемых LCD занимают около 40 мкс, а некоторые до единиц мсек. Поэтому цикл ожидания флага BF должен предшествовать совершению любой операции с LCD.

Освежив память этими принципами, начал писать код. Писал в AtmelStudio_6.1. Тестировал все на Atmega16, на древнем Дихальтовском пинбоарде.

Библиотека представляет собой два файла LCD.h и LCD.c. В LCD.h находятся все дефайны и прототипы функций. В LCD.c реализация функций. Файлы к проекту подключаются так:

Сперва файл LCD.h
#define  F_CPU 8000000UL  
#include <avr/io.h>       
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h> 
#include "LCD.h"           //Хедер для LCD дисплея

int main(void)
{
	init();		//Инициализация МК.
	LCDinit();	//Инициализация LCD, эту функцию в первую 
                        //очередь, потом все остальное.

	while(1)//Главный цикл программы.
	{
		
	}
}


Потом файл LCD.c
В окне Solution Explorer правой клавишей кликаем на папку с именем проекта. В появившемся окне выбираем Add/Existing item и наш файл который уже заранее положен в директорию проекта. Должно получится как на картинке.


После этих телодвижений библиотека подключена к проекту и можно начать настраивать дисплей.

Все настройки производятся в файле LCD.h. В файле LCD.c нужно выставить только частоту кварца.

Для начала наверное надо определиться, какая шина у дисплея будет использоваться, 4х битная или 8ми битная. После того как решили, ныряем в файл LCD.h и выбираем шину данных. Это делается раскомментированием или закомментированием #define LCD_8BIT.

>

>

>

Вот так:
//---------------------------------------------------------------------------------------------
//Если  хочешь использовать 8ми битную схему подключения, тогда раскомментируй #define LCD_8BIT
#define LCD_8BIT
//---------------------------------------------------------------------------------------------

Далее:
//Указываем порт к которому подключены выводы дисплея LCD DB0...DB7.
#define DPIN  PINC
#define DDDR  DDRC
#define DPORT PORTC	

Потом назначаем выводам дисплея, управляющие выводы микроконтроллера.
//Пины  МК      LCD   
#define DB0  0// DB0
#define DB1  1// DB1
#define DB2  2// DB2	
#define DB3  3// DB3		
#define DB4  4// DB4  
#define DB5  5// DB5
#define DB6  6// DB6
#define DB7  7// DB7 + BF флаг занятости дисплея.

Причем выводы LCD DB0...DB7 можно подключать к порту МК как угодно, хоть по порядку, хоть в разброс, хоть наперекосяк, хоть морским узлом. Это значения не имеет.

Например можно так:
//Пины  МК      LCD   
#define DB0  2// DB0
#define DB1  6// DB1
#define DB2  4// DB2	
#define DB3  7// DB3		
#define DB4  0// DB4  
#define DB5  5// DB5
#define DB6  1// DB6
#define DB7  3// DB7 + BF флаг занятости дисплея.

Далее:
//Указываем порт к которому подключены выводы дисплея E, RS, R/W.
#define CDDR  DDRD
#define CPORT PORTD

Далее:
// Указываем номера пинов МК, к которым подключаем дисплей.
#define E	4   // E     СТРОБ.
#define RW	5   // R/W   R/W=1 читаем из LCD, R/W=0 записываем в LCD.
#define RS	6   // RS    RS=0 посылаем команду в LCD, RS=1 посылаем данные в LCD.

Управляющие пины дисплея E, RS, R/W тоже как и DB0...DB7 можно подключать как угодно, на любые свободные пины МК в выбранном порту. Теперь все настроено, осталось только проверить правильность подключения вашего дисплея к МК в соответствии с файлом LCD.h.

Пару слов о 4х битном подключении.
Так как в таком подключении используются только пины данных DB4...DB7, то пины E, RS, R/W можно подключить к тому же порту где висят DB4...DB7. Так же порядок подключения пинов значения не имеет, можно хоть прямо, хоть криво. А можно E, RS, R/W подключить на другой порт. В общем как вам надо так и подключайте.

Пользовательские функции.
Настругал малек пользовательских функций, чтоб удобно было работать с дисплеем. Просто вызываешь нужную тебе функцию для нужного действия и все.
//Пользовательские функции, ими пользуемся в программе.
void LCDinit(void);			//Инициализация LCD                    
void LCDcommand(uint8_t);		//Отправка команды, настройка дисплея  
void LCDGotoXY(uint8_t, uint8_t);	//Устанавливаем курсор в X, Y позицию
void LCDdata(uint8_t);			//Вывести 1 символ на дисплей.
void LCDstring(char*i,uint8_t,uint8_t);	//Вывести строку на дисплей в позицию x,y
void LCDstring_of_sram(uint8_t*,uint8_t,uint8_t,uint8_t);//Вывести строку на дисплей в позицию x,y из ОЗУ		
void LCDstring_of_flash(const uint8_t*,uint8_t, uint8_t);//Вывести строку в позицию x,y из флеша
void LCDset(void);			//Двухстрочный дисплей 5x8 точек.
void LCDblank(void);			//Сделать невидимым инфо на дисплее
void LCDnblank(void);			//Сделать видимой инфо на дисплее + отключение видимых курсоров.
void LCDclear(void);			//Очистить дисплей от инфо + курсор на позицию 0,0
void LCDcursor_bl(void);		//Включить мигающий курсор
void LCDcursor_on(void);		//Включить подчеркивающий курсор
void LCDcursor_vi(void);		//Включить оба курсора
void LCDcursorOFF(void);		//Выключить курсор (любой)
void LCDacr(void);			//Cчетчик адреса AC всегда будет смещаться на n+1
void LCDacl(void);			//Cчетчик адреса AC всегда будет смещаться на n-1
void LCDcursorl(void);			//Сместить курсор влево на 1 символ
void LCDcursorr(void);			//Сместить курсор вправо на 1 символ
void LCDcursorln(uint8_t);		//Сместить курсор влево на n символов
void LCDcursorrn(uint8_t);		//Сместить курсор вправо на n символов
void LCDscreenl(void);			//Сместить экран влево на 1 символ
void LCDscreenr(void);			//Сместить экран вправо на 1 символ
void LCDscreenln(uint8_t);		//Сместить экран влево на n символов
void LCDscreenrn(uint8_t);		//Сместить экран вправо на n символов
void LCDscreenL(void);			//С каждым новым символом экран будет смещаться влево
void LCDscreenR(void);			//С каждым новым символом экран будет смещаться вправо
void LCDresshift(void);			//Курсор в позицию 0,0 + сброс всех сдвигов, изображение остается

Некоторые функции опишу.
LCDinit();Эта функция вызывается только один раз и вызывается самой первой, 
              при старте МК. Полезно перед этой функцией воткнуть задержку 
              на 100 мсек, чтоб питание нормализовалось. После выполнения
              этой функции LCD настроен на конкретную шину данных, 2е строки,
              5x8 точек, без отображения курсора, сдвиг курсора n+1,курсор в 
              позицию 0,0

Установка курсора в нужную позицию.
LCDGotoXY(7,1);

Первая цифра может быть от 0 до 39. Она означает позицию в строке. А вторая цифра означает строку. Если 0 верхняя строка, 1 нижняя строка. Вот карта символов дисплея.
Карта символов дисплея.
LCDGotoXY(3,1);
Цифра 3 означает четвертую позицию в любой строке. Если 0, то это первая позиция 
в любой строке. Цифра 1 означает нижнюю строку а 0 верхнюю.
|0,0|1,0|2,0|3,0|4,0|5,0|6,0|7,0| - первая строка
|0,1|1,1|2,1|3,1|4,1|5,1|6,1|7,1| - вторая строка


LCDdata('A');//Вывести 1 символ на дисплей.

LCDstring("Hi baby",0,0);//Вывести строку на дисплей в позицию x,y

Перед использованием функции LCDstring_of_sram, нужно в озу память 
      вклинить например такую строку
uint8_t text2[7] = {'H','I',' ','b','a','b','y'};
или такую, что в общемто одинаково.
uint8_t text3[7] = {"hi baby"};
Потом вызываем функцию вывода этой строки из озу
LCDstring_of_sram(text3,7,0,1); 7-это значит 7 символов в массиве. 
                                0,1 позиция вывода x y.

Перед использованием функции LCDline_of_flash, нужно фо флеш память вклинить 
например такую строку в конце не забываем символ 0 вставить, иначе эта функция 
выведет на дисплей всю флеш память МК.

const uint8_t text[] PROGMEM = {0x20,0xA8,0x70,0xB8,0xB3,0x65,0xBF,0x20,0xE3,
                                0x65,0xBF,0xBA,0x61,0x21,0};//Привет детка!

Потом эту строку из флеша выводим на LCD так:
LCDline_of_flash(text,0,1);//В позицию 0,1 на русском выведется Привет детка!, 
                             если твой LCD поддерживает русские символы.

Ну а про остальные функции особо нечего говорить. Просто их вызываешь и выполняется какое нибудь действие. Действие которое написано в комментарии к этой функции.

Тест библиотеки.
Перед тем как выложить библиотеку всенародно, я ее протестировал. Начальная схема подключения была такая:

Тестировал так:

1. Во время работающего дихальтовского пинбоарда, выдрал на ходу кварц на 8 Мгц и вставил на 16Мгц. Ничего не загнулось. Все продолжало работать, только в два раза быстрее. Я еще пару раз по ходу работы выдергивал и менял кварц то на 8 то на 16 Мгц. Вырубал/врубал питание на 16и Мгц, дисплей все равно инициализировался и продолжал работать.

2. Тогда я врубил прерывание на таймере Т0 по совпадению регистра совпадения с регистром счета. Предделитель выставил 001. Меньше уже нельзя. Ну и начал уменьшать число в регистре сравнения OCR0. Когда прерывание стало возникать каждые 12 тактов кварца, то библиотека загнулась. Причем загнулась где то на уровне функции LCDinit();. Дисплей даже не инициализировался. Я не стал рыть землю ибо в реальных проектах навряд ли прерывания возникают каждые 12 тактов. Но прикольно что на 13 тактах все работает. Когда я переключил дисплей на 8ми битную схему, то библиотека стабильно работала при любом значении в регистре OCR0. Даже когда там был 0.

Но через пару дней я все таки стал рыть землю. Как и предполагал из-за частых прерываний загиб происходит на стадии инициализации. Получается что все процессорное время фактически уходит на обработчик прерывания. Вопрос решается легко. Прерывание желательно включать тогда, когда дисплей инициализировался. Типа так:

LCDinit();
sei();//ВКЛЮЧИЛИ ПРЕРЫВАНИЕ

Но опять таки, это не обязательно, так как я выше уже сказал, что навряд ли существуют такие проекты в которых прерывание возникает каждые 13 тактов кварца. Ну а после инициализации дисплея хоть все огнем гори. Прерывания могут возникать когда угодно и как угодно, на работу дисплея это никак не влияет. Ни на 4х ни на 8ми битную шину.

3. Потом я начал собирать различные схемы подключения LCD к МК. Перепробовал разные схемы и 4х битные и 8ми. Последняя схема была 4х битная. На один порт посадил пины DB4...DB7 и E, RS, R/W. Все схемы заработали сразу.

Так что проект выкладываю в топик. Если вам нужна эта библиотека, то качайте весь проект и выдирайте оттуда эти два файла LCD.h и LCD.c и библиотэка ваша.



А если позарез нужны функции работы с памятью CGRAM? Могу вас обрадовать. Вопрос можно решить двумя способами.
1. Допишите сами эти функции. Правда неплохая идея?

2. Есть у меня еще одна библиотека, импортовая какая то. Когда то, откуда то скачал. Несколько раз использовал в довольно серьезных проектах. Все четко работает, хотя не все там сделано оптимально с точки зрения кода. И некоторые вещи сделаны не в согласии с мануалом. Однако же работает. Там тоже вагон функций, я в них даже до конца еще не разобрался. Ну и со схемами подключения там не все радужно. Так вот просто как хочется не подключишь дисплей. Постоянно надо лезть в исходники и что то там подправлять, чтоб все состыковалось с желаемой схемой подключения. Кстати когда писал свою библиотеку, то подсматривал в эту импортовую либу. Некоторые идеи оттуда содрал начисто но модернезировал. Но зато эта импортовая библиотека поддерживает работу с CGRAM. Тоже ее в топик выложил.

Кстати об объеме кода моей библиотеки. Она влезет в чип с 2мя кб памятью, еще и место останется для других дел. Но как правило дисплеи с такими МК навряд ли используются.

Кстати товарищ Signaller тестировал данную библиотеку в Протеусе. Говорит, что все вроде нормально моделируется.

Продолжение статьи здесь.
Файлы в топике: Импортовая.zip, Atmega16_LCD_LIB.zip

Комментарии (103)

RSS свернуть / развернуть
Прошу прощения, но все читать даже не стал.
Расскажите вкратце, в двух словах, чем Ваша библиотека лучше существующих?
+2
Ничем.
+2
В файле LCD.c нужно выставить только частоту кварца.
Зачем его там объявлять? Этот дефайн практически всегда задается в опциях компилятора. Лучше сделать так:
#ifndef F_CPU
#error "F_CPU not defined"
#endif

void LCDset(void); //Двухстрочный дисплей 5x8 точек.
Что мешало задать количество строк дефайном или параметром функции? Да и количество точек можно задавать, хотя ни разу не видел дисплеев с 5х10.

Интересно, почему из флеша выводятся null-terminated строки, а из ОЗУ — по размеру? Неоднородность апи — плохо.
0x65,0xBF,0xBA,0x61,0x21,0};//Привет детка!
Тарабарская грамота. Почему было не внести функцию перекодировки кириллицы из CP1251?
Ну а про остальные функции особо нечего говорить. Просто их вызываешь и выполняется какое нибудь действие.
А не слишком ли их много? К тому же, половину из них разумнее было реализовать макросами.
Я не стал рыть землю ибо в реальных проектах навряд ли прерывания возникают каждые 12 тактов.
Это даже не костыль, это просто баг.
Кстати об объеме кода моей библиотеки. Она влезет в чип с 2мя кб памятью, еще и место останется для других дел
Вообще-то, в таких случаях указывается конкретный размер в килобайтах. Одно дело полкилобайта (насколько я знаю, это около минимума для этих библиотек) и совсем другое — полтора.
+3
  • avatar
  • Vga
  • 24 июня 2013, 22:42
Тарабарская грамота. Почему было не внести функцию перекодировки кириллицы из CP1251?
Потому что она занимает место… А часто его не хватает…
Хотя, лучше её всё таки вписать, а на этапе компиляции релиза, если места будет не хватать, простым скриптом перекодировать файл с текстовыми строками и подменить макрос вызова функции на пустой.
0
Этот дефайн практически всегда задается в опциях компилятора.
Не нашел я этой опции поэтому так и сделал.

Что мешало задать количество строк дефайном или параметром функции?
LCDcommand(0b101000); пожалуйста можно так, но думаю что LCDset(); удобнее выглядит

Неоднородность апи — плохо.
Что за апи?

Почему было не внести функцию перекодировки кириллицы из CP1251?
Неплохая идея, надо будет реализовать.

половину из них разумнее было реализовать макросами.
Разумнее почему?

Это даже не костыль, это просто баг.
Что есть костыль?

указывается конкретный размер в килобайтах
Не совсем понятно как вычисляется конкретный размер? Размер чего, каждой вызываемой функции? Функция LCDinit(); весит меньше 400 байт. Все, после нее дисплей готов работать. Но в эти 400 байт входит куча других функций которые цепляет за собой LCDinit();
0
Не нашел я этой опции поэтому так и сделал.
В 4-й студии оно в настройках проекта, в пятой нужно вручную в строке дефайнов писать F_CPU=16000000L (а если и ее нет — то в «прочих опциях компилятора» -DF_CPU=16000000L).
LCDcommand(0b101000); пожалуйста можно так, но думаю что LCDset(); удобнее выглядит
Можно было сделать #define LCDset() LCDCommand(0x28). Хотя у макросов свои минусы, но по размеру кода оно было бы эффективнее.
Что за апи?
API — Application Programming Interface. Проще говоря, интерфейс библиотеки.
Разумнее почему?
Они короткие и состоят только из вызова другой функции с указанными параметрами. AFAIK без инлайнов (а они у тебя не inline, к тому же вроде свежие AVR Toolchain в коде на С не любят разворачивать инлайны при -Os) они компилируются гораздо менее эффективно, чем макрос.
Что есть костыль?
Костыль он и есть костыль.
Не совсем понятно как вычисляется конкретный размер?
Размер, на который увеличивается программа при подключении и использовании библиотеки (от одного лишь инита, например, толку нет — нужно еще текст выводить и все такое). Скажем, ЕМНИП дихальтовская библиотека для 44780 укладывается примерно в полкило. Хороший пример — раздел «системные требования» этой статьи.
0
Функция LCDinit(); весит меньше 400 байт. Все, после нее дисплей готов работать.
В МикроПаскале (правда, для PIC, для AVR лень искать проект с LCD):

LCD_Init = 78 байт;
LCD_Cmd = 87 байт;
LCD_Out = 63 байт;

Взято из статистики компиляции программы контроллера башни моего робота.
Пробовал со всеми имеющимися у меня дисплеями разных фирм, формата 8, 16, 20 символов, 1 и 2 строки.
Причем, любые ноги дисплея могут быть на любых (в том числе разных! ) портах. Задаются побитно:
var
// LCD module connections
  LCD_RS : sbit at RA3_bit;
  LCD_EN : sbit at RA4_bit;
  LCD_D4 : sbit at RD0_bit;
  LCD_D5 : sbit at RD1_bit;
  LCD_D6 : sbit at RD2_bit;
  LCD_D7 : sbit at RD3_bit;

  LCD_RS_Direction : sbit at TRISA3_bit;
  LCD_EN_Direction : sbit at TRISA4_bit;
  LCD_D4_Direction : sbit at TRISD0_bit;
  LCD_D5_Direction : sbit at TRISD1_bit;
  LCD_D6_Direction : sbit at TRISD2_bit;
  LCD_D7_Direction : sbit at TRISD3_bit;
// End LCD module connections


В большинстве виденных мной других библиотек требовалось, чтобы хотя бы входы данных были на одном порту… В вашей вроде — тоже?
0
Причем, любые ноги дисплея могут быть на любых (в том числе разных! ) портах.
Да это вещь!
В большинстве виденных мной других библиотек требовалось, чтобы хотя бы входы данных были на одном порту… В вашей вроде — тоже?
Да тоже.
0
Может свою следующую библиотеку тоже сделаю чтоб можно было подключать дисплей на разные пины любого порта.
0
Может свою следующую библиотеку тоже сделаю чтоб можно было подключать дисплей на разные пины любого порта.
Это обычно усложняет вывод, потому что данные приходится выводить не сразу байтом или полубайтом, а предварительно разбивая их побитно, но часто оно того стоит.

Например, у Меги 8 такая хреновая раскладка портов, что при использовании, например, последовательных интерфейсов I2C и UART, и АЦП, обычно не остается свободного цельнобайтового порта.

Да и разводка ног на плате часто сильно упрощается, если использовать для дисплея удобные свободные ноги.

А поскольку операции вывода на него занимают не так уж много времени в программе, то с некоторым усложнением функций ради этого вполне можно смириться.
0
Ну да согласен. Я как раз сидел и думал, что это было бы удобно применить на 8меге.
0
У меня в библиотеке так сделано. Любой пин, любого порта.
0
Так я ж и говорю, что твоя библиотека просто зверь!
0
А где ее можно глянуть?
0
0
Спасибо
0
Это в любой такой библиотеке удобно на любом мк=)
0
О, на удивление неплохо.
Впрочем, на mPAVR результаты хуже. Весь демо-проект — 775 слов (на PIC'е — 444 слова), функции:
Lcd_Cmd: 98
Lcd_Chr_CP: 12
Lcd_Init: 279
Lcd_Out: 65
Интересно, это заслуга ядра/периферии PIC или у mPPIC оптимизатор намного лучше…
Причем, любые ноги дисплея могут быть на любых (в том числе разных! ) портах. Задаются побитно:
Да, биндинг в mP довольно удобно сделан. Хотя, возможно, работая с шиной данных на одном порту (и на идущих подряд битах) можно было бы еще уменьшить размер.
0
Интересно, это заслуга ядра/периферии PIC или у mPPIC оптимизатор намного лучше…
Я использую МикроПаскаль и для PIC, и для AVR (в основном — PIC16 разные, из AVR — Меги 32, 64, 128).
Давно заметил, что для PIC те же самые подпрограммы получаются в 1,5-2 раза короче не только по размеру кода, но и по количеству команд.

В основном — из за большого количества стековых операций в коде для AVR (иногда до 20-30% кода, особенно на мелких функциях).
В PIC нет надобности сохранять регистры в стеке из за их равноправия для команд. В AVR же из за наличия специфических обособленных групп регистров, выделенных только для некоторых команд, их постоянно приходится сохранять и извлекать при вызове подпрограмм и обработке прерываний.

Хотя, возможно, работая с шиной данных на одном порту (и на идущих подряд битах) можно было бы еще уменьшить размер.
Естественно, проще и быстрее всего будет вывод на индикатор в 8-проводном режиме, сразу байтом, с ногами на одном порту, идущими по порядку. Но это, как частный случай, можно просто оформить отдельной функцией, которая будет предельно простой.

Чаще же удобнее универсальные решения, хоть они и больше и сложнее… Но иногда это — не главное, по сравнению с результатами. Особенно, если нет нормального порта со свободными ногами…
0
В PIC нет надобности сохранять регистры в стеке из за их равноправия для команд.
Точнее из-за того, что там ровно один регистр (ну, и еще статусный).
0
Точнее из-за того, что там ровно один регистр (ну, и еще статусный).
Точнее, не регистр, а скорее аккумулятор, — W, по аналогии с микропроцессорами.
У AVR — в АЛУ фактически 32 аккумулятора. Да еще — поделенных на несколько групп, и многие команды работают только с некоторыми из них.

В PIC — все регистры (ОЗУ, порты, служебные — с флагами и опциями периферии), — равноправны для всех команд. Выделенный же регистр W — (аккумулятор) в основном используется для временного хранения константы при операциях с константами, и еще некоторых действий, которые без него потребовали бы лишних операций или пересылок.

Ну, а статус — надо сохранять в любом процессоре, но только для обработки прерываний. Также в прерываниях у PIC обычно сохраняют и W (если он используется в самом обработчике прерывания).
Причем компилятор МикроПаскаля делает это сам, в программе явно этого делать не нужно.

При вызове же подпрограмм (процедур и функций), обычно сохранять ни статус, ни W, — не требуется.
0
Точнее, не регистр, а скорее аккумулятор, — W, по аналогии с микропроцессорами.
Аккумулятор — разновидность регистра. Ну и микропроцессор от микроконтроллера отличается только назначением, так что аналогия не уместна — это одно и то же. К тому же, в популярных ныне микропроцессорах ARM — 16 РОН (на самом деле 14, впрочем).
В PIC — все регистры (ОЗУ, порты, служебные — с флагами и опциями периферии), — равноправны для всех команд.
Я бы это назвал не регистрами, а оперативной памятью. Да и сам микрочип называет их RAM.
Причем компилятор МикроПаскаля делает это сам, в программе явно этого делать не нужно.
Это стандартное поведение для компиляторов, было бы странно, если бы оно было не так (тем более что средствами языка это и не сделать).
При вызове же подпрограмм (процедур и функций), обычно сохранять ни статус, ни W, — не требуется.
В принципе, у процессоров с регистровым файлом он зачастую тоже не сохраняется, по крайней мере целиком. Хотя там много вариантов, в зависимости от условий и соглашения о вызовах.
0
Аккумулятор — разновидность регистра. Ну и микропроцессор от микроконтроллера отличается только назначением, так что аналогия не уместна — это одно и то же.
Не совсем. Скорее, аккумулятор — это одна из основных частей АЛУ, тесно связанная с его логикой и статусными флагами, в отличие от группы «регистров общего назначения», функции которых по сравнению с аккумулятором были сильно урезаны. Зато, например, у Моторолловского 6800 — было аж целых 2 равноправных аккумулятора — A и B.
Я бы это назвал не регистрами, а оперативной памятью. Да и сам микрочип называет их RAM.
С точки зрения команд в PIC, — регистры портов, спецрегистры, регистры RAM — едины, находятся в общем адресном поле, поделенном на банки, частично дублируются в разных банках…

То есть, конкретной команде пофигу, с чем она работает.

А вот W — на особом положении. Он в это адресное пространство не входит, и обращение к нему особое.
так что W — не просто регистр, а именно аккумулятор в обычном понимании. Почему его назвали иначе — тут скорее всякие патентные ограничения…
Это стандартное поведение для компиляторов, было бы странно, если бы оно было не так (тем более что средствами языка это и не сделать).
Да без проблем. Достаточно вставить ассемблерную вставку из нескольких команд, которые скомпилируются без изменения.
Да и Паскалевские аналоги некоторых ассемблерных команд (например, записать константу или переменную в регистр или порт) — компилируются в такие же ассемблерные команды без дополнений. Я часто этим пользуюсь, вместо библиотечных функций, например, чтобы включить 9-битный режим USART просто установкой нужного флага, а не полной переинициализацией USART.
В принципе, у процессоров с регистровым файлом он зачастую тоже не сохраняется, по крайней мере целиком. Хотя там много вариантов, в зависимости от условий и соглашения о вызовах.
Это естественно, просто я имел в виду, что вижу много стековых команд при просмотре листинга, скомпилированного для AVR.
0
Не совсем. Скорее, аккумулятор — это одна из основных частей АЛУ, тесно связанная с его логикой и статусными флагами, в отличие от группы «регистров общего назначения», функции которых по сравнению с аккумулятором были сильно урезаны.
Я бы так не сказал. Во первых, файл РОН не менее тесно связан с АЛУ (типарегистры MCS51 в расчет не берем, у него тоже из полноправных регистров только A) и не меньше связан с логикой и статусными флагами, во вторых выполняет те же функции. У многих архитектур все регистры равноправны (у AVR есть ограничения из-за, во первых, регистров XYZ, а во вторых — в некоторых командах не хватает бит для адресации всех регистров и адресуется только верхняя половина или четверть файла).
С точки зрения команд в PIC, — регистры портов, спецрегистры, регистры RAM — едины, находятся в общем адресном поле, поделенном на банки, частично дублируются в разных банках…
Это, опять же, типично. SFR у большинства архитектур расположены в выделенной области RAM и для процессора ничем от нее не отличаются. W, разумеется, отличается — это единственный рабочий регистр самого процессора. Файловый регистр тоже на особых правах (хотя иногда его маппят и на ОЗУ, но это не более чем маппинг), отличие от акумулятора разве что одно — к РФ неприменима неявная адресация.
Нетипично в PIC только одно — это не store-n-load архитектура, как большинство RISC'ов, он умеет работать непосредственно с ОЗУ (у LnS с ОЗУ работают только команды пересылки, все остальное — только с регистрами).
Да без проблем. Достаточно вставить ассемблерную вставку из нескольких команд, которые скомпилируются без изменения.
Это уже не относится к средствам языка.
Это естественно, просто я имел в виду, что вижу много стековых команд при просмотре листинга, скомпилированного для AVR.
Это скорее объясняется тем, что на AVR mP использует стек для хранения данных, а на PIC и MCS51 — нет (а на пике его и нет, собственно).
0
а на пике его и нет, собственно
Ну это до 24-х серий. На 24-х там программный стек, и регистровый файл на 16 регистров.
0
А каким боком 16-ти битный PIC относиться к к 8-ми битникам?
0
А они mPPIC и не поддерживаются. Это другое ядро с другими компиляторами (есть еще mPdsPIC, может он поддерживает).
0
короче не только по размеру кода, но и по количеству команд
Улыбнуло=)
+1
Почему? Довольно существенное уточнение, учитывая что у младших пиков 12-битное слово, а у AVR — 16-битное. 1024 команды PIC по размеру соответствуют всего лишь 896 командам AVR.
0
1024 команды PIC по размеру соответствуют всего лишь 896 командам AVR.
Упс, это относится к PIC'ам с 14-битным словом, а не 12-битным. У PIC12 1024 команды весят столько же, сколько у AVR 768 команд (условно, не считая двусловных команд и данных в флеше).
0
А байт у пиков сколько бит?
0
Стандартный, восьмибитный.
0
А в памяти программ???
0
А в памяти программ???
У PIC16 — слово памяти программ 14 бит. Но в отличие от AVR, они занимают один адрес, читаются сразу 14 в одном такте, потому часто считаются за байт. При сохранении констант в памяти программ они занимают 8 бит из 14, но тоже 1 адрес. Поэтому например у PIC16F876 c 8К памяти программ это однозначно 8192 команды, а у Меги 8 также с 8К памяти — не более 4096, и адресация команд в AVR идет через байт, у сохраненных же данных в памяти программ — побайтно, что вносит некоторую путаницу.
Кроме того, у AVR есть и команды длиннее 2х байт…

Когда я писал про разницу команд в листингах для PIC и AVR, я имел в виду разницу в откомпилированном в МикроПаскале под соответствующий контроллер ассемблерном листинге, соответственно, количесто ассемблерных команд в однотипных процедурах.

Например, послать байт в UART, или считать принятый. У AVR ассемблерных команд на это получалось больше. В том числе, присутствовали и команды работы со стеком.
0
Сарказм детектед. Про то и разговор, что размер файла и число инструкций в обоих контроллерах прямопропорционален=)
0
Логично, но при этом программа для пика может быть длиннее в командах, но меньше в байтах. А если еще и сравнивать длину программы для пика в его словах с длиной программы для AVR в байтах…
+1
Поэтому например у PIC16F876 c 8К памяти программ это однозначно 8192 команды, а у Меги 8 также с 8К памяти — не более 4096
Тут стоит заметить, что это 8К разных единиц. У меги с 8Kw (atmega16*) памяти тоже влезет 8К команд (точнее меньше — некоторые команды двухсловные, плата за линейное АП RAM — зато исключаются команды переключения банков).
Например, послать байт в UART, или считать принятый. У AVR ассемблерных команд на это получалось больше.
Это довольно забавно, учитывая, что у AVR вроде как больше команд и теоретически то же самое можно сделать меньшим их числом (иначе неясно, зачем их столько плодить).
0
Это довольно забавно, учитывая, что у AVR вроде как больше команд и теоретически то же самое можно сделать меньшим их числом (иначе неясно, зачем их столько плодить).
Сам удивлялся… Причем я имел в виду разницу именно в количестве команд в подпрограмме, а не занимаемые ими адреса. С учетом 2х байтовых команд AVR, по адресам разница будет еще больше.

Что касается часто указываемых в списке характеристик обьемах памяти программ, то у PIC обычно просто пишут 2к, 4к, 8к. не всегда упоминая, что это в 14-битных словах. Чисто в байтах (как например это показывают программаторы PIC), — будет вдвое больше. Но раньше у PIC16 больше 8к слов и небыло. Только в PIC18 недавно появилось и 32К, и вроде даже 64К (не помню точно).

Но мне на PICах часто и 4К — за глаза, хватает. Например, в PIC16F873. Хоть я чаще ставлю 876 с 8К, — по цене они почти одинаковы. А вот на AVR когда меньше 16 Кб — как-то чувствую себя не очень уверенно… Так и хочется Мегу 16 на 32 сменить… Тем более, что имею обе. Но 32 — заметно (у нас) дороже.
0
Так и хочется Мегу 16 на 32 сменить…
Народ обычно вообще предпочитает мегу8. Те самые 4к слов, кстати.
Чисто в байтах (как например это показывают программаторы PIC), — будет вдвое больше.
В 1.75. Когда смотришь прайсы контор, где размер памяти пиков указан в байтах — крышу сносит. Вечно что-то вроде «5.685КБ флэш, 743Б ОЗУ». Алсо, размер слова еще и разный бывает — скажем, у части PIC12 слово 12-битное. Так что еще и не сразу поймешь каким коэффициентом эти килобайты в слова переводить.
0
Народ обычно вообще предпочитает мегу8. Те самые 4к слов, кстати.
Не, у Меги 8 разводка паршивая. Порты не по порядку, все перепутано… И трудно цельнобайтовый порт выделить. Я ее только для повторения чужих устройств использую (например, определитель компонентов).

В своих разработках я предпочитаю ей PIC16F873, 876. Все почти то же самое (по начинке), и тоже 28 ног, зато — ноги по порядку.

Вот у Мег 16 и 32 — прядка в ногах гораздо больше. Но 40 ног иногда — многовато…

Так что еще и не сразу поймешь каким коэффициентом эти килобайты в слова переводить.
Да там и думать нечего. У них стандартный ряд — 512 слов, 1К, 2К, 4К, 8К. Больше 8К слов появились сравнительно недавно. А поскольку одна команда — один адрес, то пофигу, сколько там бит.
И, как правило, для каждого размера корпуса есть микросхемы с разным обьемом памяти, которые легко заменяются. Например, PIC16F873-876, PIC16F874-877, PIC16F627-628, и так далее.

Так же у старых микросхем есть более современная замена, с той же цоколевкой, но большими возможностями.
Например, PIC16F84 в уже готовой плате легко сменить на PIC16F628, но у 628 кроме того что было в 84, есть еще компараторы, ШИМ, UART, дополнительный таймер…
0
Вы так уверенно говорите что у авр команду 2 байта минимум занимают, что я верю вам больше чем атмелу.
0
Вы так уверенно говорите что у авр команду 2 байта минимум занимают, что я верю вам больше чем атмелу.
Назовите хоть одну однобайтную команду у AVR.
Даже у NOP — код 16 бит.

А вот 4 байта — бывают. Например, всем известные и широко используемые CALL, JMP…

Обычно я стараюсь не писать то, в чем не уверен. Или оговариваю, что точно не помню.
0
Тю млин, совсем забыл уже. Прошу прощения, был не прав.
+1
у авр команду 2 байта минимум занимают

а разве не так? какие есть однобайтные команды (про Хэ-меги не говорю, я про них ничего не знаю:)?
0
Вы так уверенно говорите что у авр команду 2 байта минимум занимают, что я верю вам больше чем атмелу.
А разве атмел говорит не то же самое? У них даже PC адресует флеш словами, поэтому поддерживают до 128кб флеша без разбития на банки.
0
Сырая библиотека… Чтобы её прикрутить надо или подробную документацию к ней, или читать исходники…
Одни плюс, может кто нибудь из тех, у кого подобная библиотека есть и написана красиво, посмотрев на эту, решится выложить свою…
+2
Может выложишь? Интересно будет посмотреть.
0
Давай через личку её обсудим, потом посмотрим, выкладывать или как… Моя тоже не отточена до совершенства…
0
LCDstring_of_flush правильно надо писать flash
а flush это смыв в сортире
+5
Вот именно так и смываются потоки в C++.
Да и в C тоже.
Уж не знаю, в сортире или как ещё, гражданину kalobyte должно быть лучше всех известно.
0
Но ведь в данном случае kalobyte-ya прав, в названии функции действительно ошибка/опечатка. Функция выводит строку из флеш-памяти (flash), а не очищает поток ввода/вывода (flush).

Да и вообще с английским у автора некоторые проблемы (как и у меня с русским :) но это к теме не относится.
+4
Прав он и насчет flush'а, у потоков это тоже смыв содержимого из кэша в файл. Вполне даже неплохая канализационная аналогия в стиле ДиХалта получается.
+1
Исправил, правда пока только в тексте. Вечером везде исправлю.
0
Для однообразия с avr-libc стоило бы их назвать в ее стиле — LCDString и LCDString_P. of_flash длинно и некорректно (корректней from_flash).
0
Исправил, но пока только в тексте, вечером везде исправлю.
0
Исправил.
0
Так что качайте, выводите свои каракули на дисплей и радуйтесь.

Papandopala , Вы не обижайтесь, но все же я бы рекомендовал Вам сначала хорошо освоить язык С, а потом уже писать подобные статьи в тематический блог.

Понимаете, в вопросе обучения (как и в медицине), главный принцип — «не навредить». А у Вас код далек от идеала (как в вопросе проектирования API, так и в реализации самой библиотеки) и брать с него пример — не лучшая идея. Работать такая библиотека будет, но врядли ее стоит размещать в тематическом блоге, тем более что существует множество более удачных примеров кода для работы с HD44780.

Ничего личного, просто я действительно считаю, что размещать подобный код в тематическом блоге — не лучшая идея.
+2
существует множество более удачных примеров кода для работы с HD44780.
Можно какой нибудь конкретный пример, чтоб посмотреть?
0
Вот здесь есть ссылки на самые популярные библиотеки для HD-44780. Сказать какая из них самая лучшая — тяжело, т. к. они отличаются по функциям и реализации.
0
Все чета только и говорят что есть лучше, бла бла бла, так хоть бы тогда одну ссылку скинули. Я например не смог найти лучшую в свое время когда искал. Это тоже одна из причин почему решил для себя написать сам.
0
но все же я бы рекомендовал Вам сначала хорошо освоить язык С
У меня не было возможности учиться в институте 5 лет языку си. Поэтому получается так как сам понимаю на данный момент времени. Я например нарыл таки довольно много сайтов по языку си. Но это просто теория объясняющая синтаксис ну и прочее. А чтоб найти что то, где бы объяснялся стиль написания, общие принципы построения чета пока никак.
0
А чтоб найти что то, где бы объяснялся стиль написания, общие принципы построения чета пока никак.
В книжках «блаблабла С» и «блаблабла С++» ты этого и не найдешь. Надо читать книжки по общим вопросам программирования и проектирования. Такие, как «Искусство программирования» Кнута, «Алгоритмы+структуры данных=программы» Вирта, «Паттерны проектирования» GOF и так далее.
0
От себя посоветую «Совершенный код» Макконела.
Ну и можно примеры хорошего кода посмотреть. На С таких примеров много в ядре Linux.
+2
Кстати да, про него я незаслуженно забыл.
Ну и еще весьма любопытная вещь — «Жемчужины программирования» Джона Бентли.
0
За книги спасибо, вечером накачаю начну читать.
0
0
Угу, фильтруем по тегу C :бабушка и несколько преподов(интересно что они сами написали?) + еб… чий MS быстро введут любого в мир Си, для начала плодотворного говнокодирования.
0
Бабушки бывают крутые.
+1
Ну суть не в бабушке, а в том, что эти курсы в основном не учат программированию на Си(тем более эмбеддеров, т.е. «высшую рассу» кодеров), а в основном все это — компиляция «даташита» на язык Си.
0
я тоже в быдловузе год осилил, но изучал сам и начал в пту и еще раньше
просто нужна практика и что ты написал свою библиотеку — это самое полезное
нужно только ставить цели
например у меня есть библиотека для этого индикатора, которую правил я для минимизации кода и она несколько неудобна, но зато мало жрет
а есть большая библиотека для кучи индикаторов и с ней просто работать, но она и жрет прилично
0
почему здесь не запостил статьи — говнокод наверное, ссыканул?

P.S. Нужен платный ресурс, где 4-5 гуру будут шлифовать код говнокодеров. Выкладываешь код в закрытую ветку, плата $100-200 за 100-200 допустим строк кода, например. Гуру там пинают тебя со всех сторон и указывают как и что переписать — на выходе имеем удобоваримый код + обучение для нуба. Можно и за $5-20, но тогда можно открывать доступ желающим посмотреть действо, за плату, естественно — тоже $5-20.
0
Сколько врачейгуру, столько и мнений.
+1
Надо переводить это все говнокодирование и гениальное кодирование на промышленные рельсы конвеерного пр-ва с гарантированным средним качеством, но с невысокими затратами на трудоемкость для конкретного чела — гуру западло много писать, тем более массового, заурядного кода, а начинающему говнокодеру в радость пописать/попереписывать(тем более ему за это заплатят или он это как-то захочет окупить). Здесь еще надо ввести 3-е действующее лицо — вообще нуба, но заказчика.
0
Как знать, возможно вы правы.
Для должности «гуру» есть более официальное название — system architect. Например, Святослав Фёдоров был таким архитектором.
Технологизация процесса подразумевает обязательное разделение труда и упрощение каждой отдельной операции до того уровня сложности, который доступен для обычного среднего исполнителя. В этом случае мы всегда сможем использовать в работе не уникальных, а совсем обычных людей. Они обходятся значительно дешевле, и ими проще управлять.
Наш излюбленный пример технологизации бизнеса – «Макдональдс». Труд сотрудников там организован таким образом, что позволяет использовать на работе даже школьников. Что они с успехом и делают. А если у школьников «сносит крышу» – их увольняют пачками.
Но умение технологизировать прежде уникальные, ранее неповторимые процессы и есть основной труд создателя будущего успешного бизнеса.
До академика Фёдорова операции на глазах могли делать единицы уникальных хирургов, и он вполне мог бы наслаждаться своей уникальностью и навсегда остаться пусть и высокооплачиваемым, но только хирургом. Но Федоров сумел перевести этот процесс на технологические рельсы. Используя диагностическое операционное оборудование, удалось поставить эти операции «на поток». Так родился огромный и серьезный бизнес, очень нетипичный для нашей страны.
Чем заменить делегирование? Коротко и по делу
Из серии статей «Секреты управления компанией»
0
Эта статья рассказывает про низменные фокусы капитализма и учит в свою очередь новым низменным фокусам, при этом с интересом мы узнаем об изъянах капитализма — «эффективные менеджеры» с MBA дипломами, которые разрушают или уничтожают производства и кроме того, ради единоличных интересов, ухудшают жизнь об-ва в целом, например отходы чайного пр-ва в чайных пакетиках за двойную стоимость хорошего чая и т.п. И накрученные отчеты с нарисованной прибылью, т.е. хуже чем при застойном социализме.

Человека не надо загонять в какие-то тупики — разбивать работу на простые кусочки и т.п., чтобы контроллировать и эксплуатировать марионеток, которым некуда деваться. Такая конвееризация должна иметь совершенно другие цели, тем более если все это ради интереса 1-го чела или кучки людей — это аморально и неправильно, а значит рано или поздно закончится полным крахом, когда градус забитости и недовольства состоянием марионеток достигнет предела.
+1
почему здесь не запостил статьи — говнокод наверное, ссыканул?
зачем это постить тут? даже в голову не приходило постить код библиотеки известного индикатора

у меня свои критерии плохого кода, у другого свои
имея какой-то опыт любой работы ты понимаеш, что были причины сделать плохо
или так, а не этак, как обычно считается правильным

реальные проекты и проекты для себя — 2 большие разницы
0
И о коде.
Функции In, Out, Hi и прочие «системные». В хедере им делать абсолютно нечего — незачем замусоривать пространство имен вне модуля. К тому же, названия слишком короткие и общие, они не только не дают представления о том, что делают функции (в отличие от, скажем, SetPortToOut), но и конфликтуют со стандартными (Hi, например).
Далее. Раскопипащенные строки вида "((1<<DB7)|(1<<DB6)|(1<<DB5)|(1<<DB4)|(1<<DB3)|(1<<DB2)|(1<<DB1)|(1<<DB0))". Это надо делать макросом вида #define PORT_MASK ((1<<DB7)|(1<<DB6)|(1<<DB5)|(1<<DB4)|(1<<DB3)|(1<<DB2)|(1<<DB1)|(1<<DB0)), причем — не в хедере. Впрочем, на AVR этот макрос всегда будет равен 0xFF, маска реально нужна только для 4-битного режима.
Изобилие функций вида LCDblablabla(){LCDCommand(blabla)} разумней было заменить набором макросов команд #define LCD_CMD_CURSOR_OFF 0x0C и, соответсвенно, писать в коде LCDCommand(LCD_CMD_CURSOR_OFF). Впрочем, можно и наплодить макрофункций для этих задач, но сами команды все равно следует задавать константами.
В ините зачем-то скопипащен кусок SendByte, которой и в самой дофига копипасты. Плюс, достаточно странный алгоритм инициализации, не соответствующий даташиту ни от HITACHI (разработчик контроллера), ни от винстара (наиболее популярный среди радиолюбителей производитель дисплеев).
Перемешивание бит, конечно, в любом случае достаточно богатый на копипасту процесс, но можно было хотя бы вынести повторяющуюся часть в макрос (да и вообще, все повторяющееся следует выносить в макрос или функцию). Да и можно было вопользоваться более подходящей конструкцией "?:":
#define GET_BIT(inbit, outbit) (i & (1 << inbit) ? 1 << outbit : 0) //вообще, можно обойтись только номером бита и вместо outbit использовать конструкцию вида DB##bit, но не помню, нужны ли какие-то дополнительные макросы, чтобы это правильно разворачивалось
DPORT = GET_BIT(0, DB0) | GET_BIT(1, DB1) | ...

Еще местами в #ifdef 8_BIT… #else… #endif можно обойтись без первого блока, который идентичен для обоих вариантов.
+1
  • avatar
  • Vga
  • 25 июня 2013, 13:12
Меня больше смущает, что автор при передергивании строба использует фиксированную задержку в 2 NOP. Есть подозрение, что на больших тактовых частотах экран не будет работать или будет работать нестабильно. В других проектах задержку (на NOP'ах) всеже коррелируют с тактовой. Например так

// if LCD_DELAY is not defined, this definition sequence
// attempts to find a suitable LCD_DELAY given the F_CPU
#ifndef LCD_DELAY
#if F_CPU >= 16000000
#define LCD_DELAY	asm volatile ("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
#else
#if F_CPU >= 12000000
#define LCD_DELAY	asm volatile ("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
#else
...
0
а ещё лучше не считать нопы до ряби в глазах, а
__delay_cycles(F_CPU / 42);
0
Я просто привел пример из lcd.h avrlib для наглядности, идея считать NOPы мне тоже не нравится. Функция задержки лучше.
0
__delay_cycles(F_CPU / 42);
Эта функция отсюда? <util/delay.h>
0
__delay_cycles(F_CPU / 42);
Не работает чета. Компилятор вообще не знает откуда это. Есть в <util/delay.h> только __builtin_avr_delay_cycles(unsigned long);
0
О интересная реализация. Надо это запомнить на будущее. Хотя да, я вот тоже все думаю что два нопа это мало. Я сейчас сел и посчитал, на 16 Мгц надо 8 нопов. А у меня два всего. Хотя на 16 Мгц работает, проверял.
0
Неужели совсем все так плохо?
0
Не так давно я писал тут, что если код решает задачу — то это хороший код. С другой стороны — нет предела совершенству. Выбор за тобой — полировать или оставить как есть. Я только описал часть того, что сделал бы иначе.
Но это чуть меньший ужас, чем код твоего «ПЛК». Наиболее неприятное — слишком большая область видимости некоторых вещей («системных» функций в частности), большие число магических чисел (для этого есть константы) и много копипасты.
И все же, сколько весит скомпилированный код?
0
Но это чуть меньший ужас
Хоть что то приятное…

и много копипасты
я и так старался ее избегать.

И все же, сколько весит скомпилированный код?
Не совсем понятно как это узнать. Если вызвать все пользовательские функции, то много наверное. если только некоторые то не очень.

слишком большая область видимости некоторых вещей («системных» функций в частности)
Так а куда их девать? Прототипы этих функций просто убрать из LCD.h?

Это надо делать макросом вида #define PORT_MASK...
Так как решить, когда макросом когда нет?

да и вообще, все повторяющееся следует выносить в макрос или функцию
Что является критерием куда именно выносить, в макрос или функцию?
0
Здесь уже предлагали глянуть исходники ядра Linux для примеров хорошего кода.
 В свою очередь, с тех времён, когда я писал под X Window System, у меня остались весьма приятные воспоминания об этой системе с точки зрения программиста. В частности, я довольно плотно занимался Xlib, Xaw, X Motif. Код написан на чистом C, но таким хорошо структурированным образом, что реализуются «три кита» объектно-ориентированного программирования: инкапсуляция, наследование и полиморфизм. То, что как раз хорошо подходит для событийного, иерархического, оконного, графического интерфейса и, как правило, не мыслится без применения C++.
 Другое дело, что это мало что даст — просто пялиться в пусть даже хороший код без понимания, для каких целей и как это должно работать. Примеров хорошего кода для микроконтроллеров я, к сожалению, посоветовать не могу.
0
Не совсем понятно как это узнать. Если вызвать все пользовательские функции, то много наверное. если только некоторые то не очень.
Хотя бы скажи сколько скомпилированный пример весит (после компиляции компилятор выводит статистику, сколько какой памяти выюзано). Подробный список по функциям есть в .map-файле, правда в малочитабельном виде.
Так а куда их девать? Прототипы этих функций просто убрать из LCD.h?
Да. Если начнет жаловаться «не найдено» — нужно перенести их в начало .c-файла или поместить в начале .c-файла их объявления (forward declarations). К тому же, неплохо бы переименовать функции так, чтобы было понятней, что они делают.
Так как решить, когда макросом когда нет?
Это здоровенное выражение, вычисляющееся константой на этапе компиляции и многократно используемое в тексте. Такие вещи следует объявлять именованной константой. В С ее роль обычно выполняют макросы или enum'ы.
Что является критерием куда именно выносить, в макрос или функцию?
А вот это уже не столь простой вопрос. Здесь нужно понимать чем различаются функции и макрофункции (макросы), а также достоинства и недостатки обоих.
В целом, выносить лучше в функцию, но некоторые вещи из-за требований оптимизации приходится делать макросами. В принципе, вместо макросов можно использовать inline-функции, но компилятор может счесть что он умнее и отказаться их разворачивать (иногда он прав, иногда ошибается). В С++ практически исключительно используются inline-функции, в С часто применяются макрофункции.
0
Хотя бы скажи сколько скомпилированный пример весит
полтора кило.
Да. Если начнет жаловаться «не найдено»
Как раз таки жалуется. Перенесу их вечером в *.с файл.
0
полтора кило.
Много.
Как раз таки жалуется. Перенесу их вечером в *.с файл.
Конечно жалуется, функция должны быть объявлена до использования. Перенеси объявления из .h файла в .c файл, размести непосредственно перед первой функцией.
0
Пользуюсь библиотечкой, которую как то нашел в инете. Все хорошо, но в протеусе в упор не запускается, либо запускается с косяками. А как с вашей либой?
0
Исходя из рекомендаций выше, я хочу на днях ее модернезировать и улучшить. Потом выложу сюда же обновленный вариант. Скачайте и попробуйте. Я с протеусом вообще не работаю. Даже не знаю что сказать.
0
OK, попробую.
0
Кстати когда попробуешь, напиши что получилось, интересно просто. Я когда выложу обновленную, напишу тебе.
0
Вот в этой теме в комментах приведены ссылки на разные варианты таких библиотек, в том числе для C++: HD44780. Библиотека для avr-gcc.

Мой вариант: LCD.cpp и LCD.h.
0
  • avatar
  • uni
  • 27 июня 2013, 03:14
Твоя библиотека просто зверь!
0
Под отладчиком она не так страшна. В Proteus'е можно прогнать основной функционал, кроме ожидания бита при чтении данных из индикатора, вроде это не работало и нужно отключать, если там моделируешь. Она компилируется для IAR и GCC, поэтому там есть некоторые излишества с этим связанные. Может быть при переходе на 4.7 версию удастся привести всё к более привычному виду.

Эта ссылка из тестового проекта, кстати: HD44780. Проект для IAR 6.10 находится в /iar6/, проект для Proteus'а в корне. Там функционал — это анимация, когда изображение из невидимой области переходит в видимую и отображение версии прошивки (эта часть уже немного устарела, т.к. это можно сейчас сделать проще).

Библиотека ещё до конца не доделана, там нужно прибраться в коде и заголовочниках, да и примеров добавить не мешало бы.
0
Сделал небольшие правки проекта HD44780. Makefile настроен так, чтобы не включать код функций, которые не используются в программе, поэтому на основе этой статистики нельзя делать какие-то предположения и сравнения.

AVR Memory Usage
----------------
Device: atmega16
Program:    1684 bytes (10.3% Full)
(.text + .data + .bootloader)
Data:          9 bytes (0.9% Full)
(.data + .bss + .noinit)

Вот так он работает в Proteus 7.7 SP2.

Тестовый проект HD44780

Основной код примера (без инициализации и прочего, все строки во флеш находятся). Строка версии немного съехала (не вошла), т.к. до этого я по другому её формировал. Анимация у меня в железе нормально происходит, а в Proteus делается наоборот почему-то. Объяснения этому пока не нашёл.

// -=[ Постоянные во флеш-памяти ]=-

// Описание заставки
FLASHSTR_DECLARE( char, frmSplashString,
#ifdef __GNUC__
"                 HD44780 Demo GCC\n"
#elif defined __ICCAVR__
"                 HD44780 Demo IAR\n"
#endif
"                 Author: uni     " );

// Статическая информация на экране
FLASHSTR_DECLARE( char, frmString,
"Ver:            \n"
"                " );


// -=[ Переменные в ОЗУ ]=-


/***********************
*  Р Е А Л И З А Ц И Я
*  ~~~~~~~~~~~~~~~~~~~
************************/


/**
 * Главный (основной) поток программы
 */
HRESULT CMCU::MainThreadProcedure(){

    // Вывод заставки
	CLCD::WriteString( frmSplashString, 0, 0 );
	
    // Анимация заставки
	uint8_t cnt = 16;
	
	do {
	
	    CLCD::ShiftLeft();
	    _delay_ms( 250 );
	
	} while ( cnt-- );
	
	_delay_ms( 3000UL );

    CLCD::Clear();
    CLCD::Home();
    CLCD::WriteString( frmString, 0, 0 );

    // Вывод версии
    CLCD::WriteString( CVersion::GetBuildDateString(), 0, 5 );

    // Разрешаем прерывания
    __enable_interrupt();

    do {


    } while ( true );

    // Все проверки прошли успешно, объект в рабочем состоянии
    return NO_ERROR;

}
0
вброшу.
вот как надо делать
собсно, там прерывания 500 раз в сек, из обработчика вызывается
if(hd44780_flags & HD44780_TX) hd44780_tx_proc();

500 символов в сек — это примерно 15 FPS, что довольно неплохо.
и никаких delay()
0
А для дисплея 20х4/40х2?
А зачем тогда krn_sleep(1)?
Ваш код при высокой нагрузке будет сильнее тормозить выполнение.
0
это слип на 1 тик, т.е. 1/500 сек.
тормозов от этого нет, этот слип внутри ядра решедулит очередь тредов и передаёт управление следующему треду на 1 тик.
0
да и потом, вывод в дисплей весь внутри обработчика прерывания, там вообще никаких слипов нет.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.