Подключаем «отечественный» LCD 16x2 MT-16S2S по SPI. Часть 2. Программа
Продолжение темы про работу с индикатором от фирмы МЭЛТ МТ-16S2S на ST7070.
Первая часть.
В этой части будут разобраны основные команды библиотеки. А именно: инициализация, передача команды (сдвиг, очистка, курсор), вывод символа, вывод строки, создание своего символа.
Вот что нам досталось от производителя:
Main:
call Init16S2S
clr CS
mov A,#80h ;Set DDRAM address
call Byte ;»_ «
mov A,#3Ch ;Function set
call Byte ;EXT=1
mov A,#83h ;Set data length
call Byte ;Дальше будут 4 байта данных
mov A,#'M'
call Byte ;»M_ «
mov A,#'E'
call Byte ;»ME_ «
mov A,#'L'
call Byte ;»MEL_ «
mov A,#'T'
call Byte ;»MELT_ «
mov A,#38h ;Function set
call Byte ;EXT=0 для последующих команд
mov A,#14h ;Cursor or display shift
call Byte ;»MELT _ «
setb CS
Init16S2S:
clr PSB ; Последовательный интерфейс
call Delay40ms
setb SCL ; Начальное значение сигнала
clr CS ; Начало последовательности команд
mov A,#38h ; Function set
call Byte ;DL=1, EXT=0
mov A,#06h ; Entry mode set
call Byte ; I/D=1,SH=0
mov A,#0Eh ; Display ON/OFF control
call Byte ; D=1,C=1,P=0
mov A,#01h ; Clear display
call Byte
setb CS ; Конец последовательности команд
call Wait1.5ms
ret
Byte:
mov C,ACC.7
clr SCL
mov SI,C
setb SCL
mov C,ACC.6
clr SCL
mov SI,C
setb SCL
mov C,ACC.5
clr SCL
mov SI,C
setb SCL
mov C,ACC.4
clr SCL
mov SI,C
setb SCL
mov C,ACC.3
clr SCL
mov SI,C
setb SCL
mov C,ACC.2
clr SCL
mov SI,C
setb SCL
mov C,ACC.1
clr SCL
mov SI,C
setb SCL
mov C,ACC.0
clr SCL
mov SI,C
setb SCL
Wait40us:
mov A,#20 ; Для 12МГц тактовой частоты
djnz ACC,$
ret
Wait1.5ms:
mov R0,#38
$1:
call Wait40us
djnz R0,$1
ret
В общем, не густо.
Но главное — есть последовательность и описание команд.
Итак, инициализация выглядит так:
Требуется выставить CS в 0 и передать 4 байта: 38h, 0Eh, 01h, 06h.
А затем снова выставить CS в 1.
Это для включения с опциями по-умолчанию.
Здесь можно учитывать команды управления, приведенные в листе данных.
Первая команда 38h это переход в Function Set — Режим настроек. EXT=0, DL нам не важен, так как последовательный режим установлен выводом PSB.
Вторая команда 0Eh включает дисплей и курсор, устанавливает нулевую страницу знакогенератора
Третья 01h очищает дисплей.
Четвертая 06h переводит в режим Entry Mode Set и заодно устанавливает направление сдвига вправо.
Все команды после сброса требуют 40 мкс задержки. Очистка дисплея — 1,52 мс. Поэтому, чтобы исключить мерцание, лучше заполнять дисплей пробелами вместо очистки. Или выводить только изменившиеся знаки. А команду очистки использовать только при включении. Или «смене страницы меню», когда позволительно полное исчезновение изображения. Может показаться, что 1,5 мс это незаметно на глаз, но использование команды очистки неизменно заставляет изображение мерцать (возможно, я не умею ее использовать).
Фактически, инициализация — это просто передача команды дисплею, поэтому я не стал делать ее отдельной процедурой. Кроме того, команда установки страницы знакогенератора и включения курсора передается вместе с командой включения самого дисплея. (Display ON/OF).
Создал несколько флагов для передачи команд. Флаги разбиты по группам в соответствии с тем, какие из них могут передаваться одновременно. Например, первые шесть по одному из пары передаются в одной команде (Display ON/OF)
const u16 LCD_cmd_CUR_OFF = 0x8000;
const u16 LCD_cmd_CUR_ON = 0x4000;
const u16 LCD_cmd_DISP_OFF = 0x2000;
const u16 LCD_cmd_DISP_ON = 0x1000;
const u16 LCD_cmd_SetPage_0 = 0x0800;
const u16 LCD_cmd_SetPage_1 = 0x0400;
const u16 LCD_cmd_CUR_SHIFT = 0x200;
const u16 LCD_cmd_DISP_SHIFT = 0x100;
const u16 LCD_cmd_SHIFT_LEFT = 0x80;
const u16 LCD_cmd_SHIFT_RIGHT = 0x40;
const u16 LCD_cmd_Home = 0x0002;
const u16 LCD_cmd_ClrScr = 0x0001;
И тогда процедура передачи команды инициализации (а это включение, настройка курсора, сдвиг и прочее, описанное в листе данных) выглядит следующим образом.
void LCD_Cmd(u16 cmd)
{
uint8_t toSend ;
toSend = 0×8;
GPIO_ResetBits(CS_Port, CS_Bit);
Delay_mks(50);
SPI_Send_byte(0×38); Delay_mks(50);
//Очистка дисплея
if ((cmd&LCD_cmd_ClrScr) >0){SPI_Send_byte(0×01); Delay_mks(50); };
//Курсор в начало
if ((cmd&LCD_cmd_Home) >0){SPI_Send_byte(0×02); Delay_mks(50); };
// Включение
if (cmd > 0×3ff){
toSend = toSend+((cmd&LCD_cmd_CUR_ON) >> 13)+ // курсор
((cmd&LCD_cmd_DISP_ON) >> 10)+ // дисплей
((cmd&LCD_cmd_SetPage_1) >> 10); // кодовая страница 1
SPI_Send_byte(toSend); Delay_mks(50);
};
cmd = cmd&0×3ff;
toSend = 0×10;
// Сдвиг
if (cmd > 0×3f){
toSend += ((cmd&LCD_cmd_DISP_SHIFT) >> 5)+ // Сдвиг дисплея
((cmd&LCD_cmd_SHIFT_RIGHT) >> 4); // вправо
SPI_Send_byte(toSend); Delay_mks(50);
};
SPI_Send_byte(0×38); Delay_mks(50);
GPIO_SetBits(CS_Port, CS_Bit);
}
Команда включения дисплея будет выглядеть так:
LCD_Cmd(LCD_cmd_CUR_OFF | LCD_cmd_DISP_ON | LCD_cmd_SetPage_0);
Процедура для вывода символа в произвольную позицию (требует три байта: строка, столбец, код символа). Вывод «своего» символа, когда он уже записан, осуществляется этой же процедурой. Последовательность такая: первый байт (x) — адрес позиции, в которой будет отображаться символ.
Следует учесть единицу в старшем разряде команды установки адреса.
Второй байт — переход в расширенный режим (EXT=1)
Третий байт — 80h + (число символов-1), в данном случае символ всего один, поэтому 80h.
Четвертый байт — собственно код символа из установленной выше страницы знакогенератора (вот где засада)
Пятый — возврат в обычный режим (EXT=0).
void LCD_SET_PUTChar(uint8_t y, uint8_t x, char code)
{
x+=0×80-1+(y-1)*0×40;
GPIO_ResetBits(CS_Port, CS_Bit); Delay_mks(50);
SPI_Send_byte(x); Delay_mks(50);
SPI_Send_byte(0×3c); Delay_mks(50);
SPI_Send_byte(0×80); Delay_mks(50);
SPI_Send_byte(code); Delay_mks(50);
SPI_Send_byte(0×38); Delay_mks(50);
GPIO_SetBits(CS_Port, CS_Bit);
}
Процедура для записи своего символа.
Записывает 8 байт для одного символа. Пока не оптимально, стоит переделать по аналогии с выводом целой строки.
void LCD_SET_CustomChar(uint8_t code, const char* simbol)
{
uint8_t a, adr;
for( j=0; j<8; j++){
adr =0×40+j+(code<<3);
GPIO_ResetBits(CS_Port, CS_Bit); Delay_mks(50);
SPI_Send_byte(adr); Delay_mks(50);
SPI_Send_byte(0×3c); Delay_mks(50);
SPI_Send_byte(0×80); Delay_mks(50);
SPI_Send_byte(simbol[j]); Delay_mks(50);
SPI_Send_byte(0×38); Delay_mks(50);
GPIO_SetBits(CS_Port, CS_Bit);
};
}
Адрес для записи от 0 до 7. В качестве символа передается массив из 8 байт — графическое изображение символа. Его, конечно, можно получить вручную, но можно воспользоваться одним из множества готовых генераторов. Например таким:
И, наконец, вывод текста. Также с позиции строка-столбец.
Так же, адрес первого символа, затем переход в расширенный режим, число байт, сами байты, возврат в EXT=0.
void LCD_print_text(uint8_t y, uint8_t x, char* text)
{
u8 nBytes;
if (! text) return;
nBytes=strlen(text);
if (nBytes>80) return;
x+=0×80-1+(y-1)*0×40;
GPIO_ResetBits(CS_Port, CS_Bit); Delay_mks(50);
SPI_Send_byte(x); Delay_mks(50);
SPI_Send_byte(0×3c); Delay_mks(50);
SPI_Send_byte(0×80+nBytes-1); Delay_mks(50);
for( j=0; j<nBytes; j++){
SPI_Send_byte(text[j]); Delay_mks(50); };
SPI_Send_byte(0×38); Delay_mks(50);
GPIO_SetBits(CS_Port, CS_Bit);
}
Код написан в Keil для STM32. Настройка переферии стандартная для ARM-контроллеров.
P. S.…, а я ведь не настоящий сварщик, я книгу по программированию в интернете нашел…