STM32L–Контроллер ЖКИ дисплея

8 Декабря 2011 К комментариям

При знакомстве с STM8L ЖКИ контроллер я обошел стороной, но сейчас решил восполнить данный пробел на примере STM32L-Discovery, тем более, что на плате уже имеется хороший ЖКИ индикатор.

image

ЖКИ индикатор

Установленный на плате ЖКИ индикатор имеет шесть 14-ти сегментных знаков и дополнительно колонку из четырех полос:

image

Сегменты индикатора объединены в четыре группы (COM0-COM3) по 24 сегмента в каждой (SEG0-SEG23).

Схема подключение индикатора к линиями ввода-вывода микроконтроллера

image

Соответствие сегментов указанно в документации к плате:

UM1079: STM32L-DISCOVERY (Руководство пользователя)

ЖКИ контроллер

Контроллер предназначен для управления монохромными жидкокристаллическими индикаторами.

Краткие характеристики:

  • настраиваемая частота обновления
  • поддержка статического и мультиплексированного режима управления (до восьми групп)
  • поддержка нескольких уровней управляющего напряжения (до четырёх)
  • двойная буферизация
  • программная установка контраста
  • и т.д.

В принципе контроллер не очень сложный и хорошо описан в документации:

RM0038: STM32L151xx and STM32L152xx advanced ARM-based 32-bit MCUs (Руководство пользователя)

Поэтому описание его я пропускаю и сразу перехожу к практической работе.

Настройка ЖКИ контроллера для работы ЖКИ индикатором

Как и всегда начинаем с тактирования модуля.

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

  • LSI – генератор
  • LSE – генератор
  • HSI – генератор (с предварительным делением частоты 2, 4, 8 или 16)

Для простоты выберем встроенный низкочастотный генератор (LSI):

image

Активируем и дождемся готовности:

// включаем LSI-генератор
RCC->CSR |= RCC_CSR_LSION;
while (!(RCC->CSR & RCC_CSR_LSIRDY))
{
}

Однако напрямую (сразу) указать LSI-генератор в качестве источника тактовых сигналов ЖКИ модуля нельзя, так как тактирование объедено с часами реального времени (RTC) и приоритет отдан им (это видно по названию сигнала RTCCLK и битов выбора тактовых частот RTCSEL), а в виду специфики работы RTC модуля, необходимо предварительно (после сброса мк) “получить” доступ к битам RTCSEL.

Что бы получить доступ необходимо проделать следующие действия:

// разрешить тактирование модуля управлением питанием
RCC->APB1ENR |= RCC_APB1ENR_PWREN;

// отключить защиту
PWR->CR |= PWR_CR_DBP;

// выбрать источник тактовой частоты
RCC->CSR |= RCC_CSR_RTCSEL_LSI;

Для работы с регистрами самого модуля, разрешаем их тактирование:

RCC->APB1ENR |= RCC_APB1ENR_LCDEN;

Описываем линии ввода-вывода (согласно схеме подключения):

#define PIN_LCDSEG0		A, 1, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG1		A, 2, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG2		A, 3, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG3		B, 3, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG4		B, 4, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG5		B, 5, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG6		B, 10, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG7		B, 11, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG8		B, 12, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG9		B, 13, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG10	B, 14, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG11	B, 15, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG12	A, 15, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG13	B, 8, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG14	C, 0, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG15	C, 1, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG16	C, 2, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG17	C, 3, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG18	C, 6, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG19	C, 7, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG20	C, 8, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG21	C, 9, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG22	C, 10, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define PIN_LCDSEG23	C, 11, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define LCDCOM0			A, 8, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define LCDCOM1			A, 9, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define LCDCOM2			A, 10, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11
#define LCDCOM3			B, 9, HIGH, MODE_AF_PUSH_PULL, SPEED_400KHZ, AF11

Производим настройку:

PIN_CONFIGURATION(PIN_LCDSEG0);
PIN_CONFIGURATION(PIN_LCDSEG1);
PIN_CONFIGURATION(PIN_LCDSEG2);
PIN_CONFIGURATION(PIN_LCDSEG3);
PIN_CONFIGURATION(PIN_LCDSEG4);
PIN_CONFIGURATION(PIN_LCDSEG5);
PIN_CONFIGURATION(PIN_LCDSEG6);
PIN_CONFIGURATION(PIN_LCDSEG7);
PIN_CONFIGURATION(PIN_LCDSEG8);
PIN_CONFIGURATION(PIN_LCDSEG9);
PIN_CONFIGURATION(PIN_LCDSEG10);
PIN_CONFIGURATION(PIN_LCDSEG11);
PIN_CONFIGURATION(PIN_LCDSEG12);
PIN_CONFIGURATION(PIN_LCDSEG13);
PIN_CONFIGURATION(PIN_LCDSEG14);
PIN_CONFIGURATION(PIN_LCDSEG15);
PIN_CONFIGURATION(PIN_LCDSEG16);
PIN_CONFIGURATION(PIN_LCDSEG17);
PIN_CONFIGURATION(PIN_LCDSEG18);
PIN_CONFIGURATION(PIN_LCDSEG19);
PIN_CONFIGURATION(PIN_LCDSEG20);
PIN_CONFIGURATION(PIN_LCDSEG21);
PIN_CONFIGURATION(PIN_LCDSEG22);
PIN_CONFIGURATION(PIN_LCDSEG23);
PIN_CONFIGURATION(LCDCOM0);
PIN_CONFIGURATION(LCDCOM1);
PIN_CONFIGURATION(LCDCOM2);
PIN_CONFIGURATION(LCDCOM3);

Теперь можно перейти к настройке режима работы ЖКИ контроллера.

Используемый индикатор работает в мультиплексированном режиме (читать) с четырьмя общими управляющими линиями и четырьмя уровнями управляющих напряжений (не уверен в правильности формулировки).

Диаграммы управляющих сигналов в данном режиме (1/3 bias, 1/4 duty):

image

В зависимости от режима работы индикатора необходимо правильно выбрать частоту обновления, я не стал рассчитывать самостоятельно, а просто воспользовался примером из документации:

image

Реально частота будет немного выше, так как частота LSI-генератора равна 37 кгц, но это некритично.

Производим настройку режима работы и установку предварительных делителей, согласно выше сказанному:

LCD->CR &= ~LCD_CR_DUTY;
LCD->CR |= LCD_CR_DUTY_1 | LCD_CR_DUTY_0;

LCD->CR &= ~LCD_CR_BIAS;
LCD->CR = LCD_CR_BIAS_1;

LCD->FCR &= ~LCD_FCR_PS;
LCD->FCR |= (4 >> 22);

LCD->FCR &= ~LCD_FCR_DIV;
LCD->FCR |= (1 >> 18);

Насколько я понял из документации после записи в регистр LCD_FCR необходимо дождаться обновления данных, проверяя бит FCRSF: LCD Frame Control Register Synchronization flag в регистре LCD_SR:

while ((LCD->SR & LCD_SR_FCRSR) == 0)
{
}

Далее необходимо выбрать источник управляющего напряжения (внешний или внутренний) и задать уровень контраста. На плате изначально не установлен резистор R29:

image

поэтому, будем использовать встроенный источник:

LCD->CR &= ~LCD_CR_VSEL;

Уровень контраста выберем “средний”:

LCD->FCR &= ~LCD_FCR_CC;
LCD->FCR |= LCD_FCR_CC_1;

Для лучшего понимания отмечу изменения (настройки) графически:

LCD control register (LCD_CR)

image

imageimageimage

LCD frame control register (LCD_FCR)

image

Остальные настройки ЖКИ контроллера остаются по умолчанию.

На последнем этапе разрешаем работу ЖКИ контроллера:

LCD->CR |= LCD_CR_LCDEN;

Вот и вся настройка, вроде не сложно. Улыбка

Таблица перекодировки

Составить таблицу кодировки символов долгий процесс.

Можно пойти двумя путями:

  • взять за основу документацию
  • использовать простой метод перебора

Я расскажу как пойти по первому пути.

Взять таблицу подключения и обозначение сегментов из документации:

image

image

image

Как видим довольно сложное подключение.

Особенно, если учесть несоответствие между позиционным обозначением сегментом ЖКИ контроллера и индикатора.

Индикаторов ЖКИ контроллер
SEG0 SEG0
SEG1 SEG1
SEG2 SEG2
SEG3 SEG7
SEG4 SEG8
SEG4 SEG9
SEG6 SEG10
SEG7 SEG11
SEG8 SEG12
SEG9 SEG13
SEG10 SEG14
SEG11 SEG15
SEG12 SEG17
SEG13 SEG16
SEG14 SEG18
SEG15 SEG19
SEG16 SEG20
SEG17 SEG21
SEG18 SEG24
SEG19 SEG25
SEG20 SEG26
SEG21 SEG27
SEG22 SEG40
SEG23 SEG41

Попробую объяснить как составить таблицу или как “это” работает.

Сегменты нашего индикатора объединены в четыре группы (COM0-COM3) по 24 сегмента в каждой (SEG0-SEG23).

Грубо режим работы индикатора можно сравнить с динамической индикацией, т.е. когда контроллер поочередно выбирает группы сегментов и для каждой группы устанавливает свои данные на линиях SEG0-SEG23.

О динамическом режиме работы можно прочитать тут.

Таким образом нам необходимо иметь четыре буфера с данными для каждой группы.

В ЖКИ контроллере имеется “видео” память – 16 ячеек по 32 бита. (RAM[0] - RAM[15])

Один буфер занимает в памяти две ячейки, т.е. 64 бита. Для нашего режима получим:

группа номера ячеек
COM0 RAM[0]RAM[1]
COM1 RAM[2]RAM[3]
COM2 RAM[4]RAM[5]
COM3 RAM[6]RAM[7]

Для простоты работы разработчики сделали привязку обозначений сегментов (номеров) к номеру бита.

Например, для того, что бы активировать (зажечь) сегменты линии SEG3, нужно установить бит за номером 3.

Резюмируя все выше сказанное, для вывода цифры “1” (один) в первом разряде нашего индикатора, необходимо активировать сегменты 1B и 1C.

Смотрим таблицу приведенную выше и находим сегменты 1B и 1C, записываем группу и номер сегмента, далее смотрим соответствие между позиционным обозначением сегментом ЖКИ контроллера и индикатора (за эту операцию вероятно вы должны вспоминать нехорошим словом трассировщика платы):

Сегмент Группа Номер сегмента индикатора Номер
сегмента ЖКИ контроллера
1B COM0 LCDSEG22 SEG40
1C COM1 LCDSEG1 SEG1
Из чего следует, что нужно установить бит 8 в RAM[1] (40 – 32 = 8) и бит 1 в RAM[2].

Однако данные не отобразятся сразу, так как “видео” память это промежуточный буфер (выше я писал, что ЖКИ контроллер имеет двойную буферизацию вывода), поэтому до записи нужно убедится в окончании предыдущей пересылке, а после сделать новый запрос:

// ожидаем завершения предыдущего вывода данных 
while(LCD->SR & LCD_SR_UDR)
{
}
	
// цифра 1 в первом разряде
LCD->RAM[1] = BIT(8);
LCD->RAM[2] = BIT(1);

// запрос вывода новых данных
LCD->SR |= LCD_SR_UDR;

Ещё одно замечание: видим, что два сегмента выбиваются из 32-ух бит (SEG41 и SEG40), эту ситуацию позволит исправить “замена” (remap) данных сегментов, а точнее группы сегментов SEG40-SEG43 на SEG28-SEG31 (данное замечание справедливо только для данных линий, более детально смотрите документацию). Для этого нужно установить бит MUX_SEG:

LCD->CR |= LCD_CR_MUX_SEG

В нашем случае перенос позволит использовать для кодирования символов только четыре ячейки памяти RAM[0], RAM[2], RAM[4] и RAM[6], а так же сократит расход памяти на таблицу символов.

На данный момент я сам ещё не составил таблицу.

Вот вкратце и все о работе с ЖКИ контроллером.


Заключение

Если не спешить и внимательно читать документацию, то проблем при работе с ЖКИ контроллером (и не только) у вас не должно возникнуть. В любом случае вы всегда можете получить помощь на форумах, можно конечно написать мне, но я сам только учусь Улыбка


Исходный код примера

Код целиком:

//------------------------------------------------------------------------------
int main(void)
{
	RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN;
	RCC->APB1ENR |= RCC_APB1ENR_LCDEN | RCC_APB1ENR_PWREN;

	// включаем LSI-генератор
	RCC->CSR |= RCC_CSR_LSION;
	while (!(RCC->CSR & RCC_CSR_LSIRDY))
	{
	}

	// светодиоды
	PIN_CONFIGURATION(LED_GREEN);
	PIN_CONFIGURATION(LED_BLUE);

	// кнопка
	PIN_CONFIGURATION(USER_BUTTON);

	// ЖКИ индикатор
	PIN_CONFIGURATION(PIN_LCDSEG0);
	PIN_CONFIGURATION(PIN_LCDSEG1);
	PIN_CONFIGURATION(PIN_LCDSEG2);
	PIN_CONFIGURATION(PIN_LCDSEG3);
	PIN_CONFIGURATION(PIN_LCDSEG4);
	PIN_CONFIGURATION(PIN_LCDSEG5);
	PIN_CONFIGURATION(PIN_LCDSEG6);
	PIN_CONFIGURATION(PIN_LCDSEG7);
	PIN_CONFIGURATION(PIN_LCDSEG8);
	PIN_CONFIGURATION(PIN_LCDSEG9);
	PIN_CONFIGURATION(PIN_LCDSEG10);
	PIN_CONFIGURATION(PIN_LCDSEG11);
	PIN_CONFIGURATION(PIN_LCDSEG12);
	PIN_CONFIGURATION(PIN_LCDSEG13);
	PIN_CONFIGURATION(PIN_LCDSEG14);
	PIN_CONFIGURATION(PIN_LCDSEG15);
	PIN_CONFIGURATION(PIN_LCDSEG16);
	PIN_CONFIGURATION(PIN_LCDSEG17);
	PIN_CONFIGURATION(PIN_LCDSEG18);
	PIN_CONFIGURATION(PIN_LCDSEG19);
	PIN_CONFIGURATION(PIN_LCDSEG20);
	PIN_CONFIGURATION(PIN_LCDSEG21);
	PIN_CONFIGURATION(PIN_LCDSEG22);
	PIN_CONFIGURATION(PIN_LCDSEG23);
	PIN_CONFIGURATION(LCDCOM0);
	PIN_CONFIGURATION(LCDCOM1);
	PIN_CONFIGURATION(LCDCOM2);
	PIN_CONFIGURATION(LCDCOM3);

	// снимаем блокировку
	PWR->CR |= PWR_CR_DBP;

	// выбираем LSI генератор в качестве источника тактовых сигналов для ЖКИ (RTC)
	RCC->CSR |= RCC_CSR_RTCSEL_LSI;

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	LCD->CR &= ~LCD_CR_BIAS;
	LCD->CR = LCD_CR_BIAS_1;
	//
	LCD->CR &= ~LCD_CR_DUTY;
	LCD->CR |= LCD_CR_DUTY_1 | LCD_CR_DUTY_0;

	LCD->FCR &= ~LCD_FCR_PS;
	LCD->FCR |= (4 >> 22);
	//
	LCD->FCR &= ~LCD_FCR_DIV;
	LCD->FCR |= (1 >> 18);
	//
	LCD->FCR &= ~LCD_FCR_CC;
	LCD->FCR |= LCD_FCR_CC_1;

	while ((LCD->SR & LCD_SR_FCRSR) == 0)
	{
	}

	//
	LCD->CR &= ~LCD_CR_VSEL;
	LCD->CR |= LCD_CR_MUX_SEG;

	//
	LCD->CR |= LCD_CR_LCDEN;

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	// ожидаем завершения предыдущего вывода данных
	while(LCD->SR & LCD_SR_UDR)
	{
	}

	// цифра 1 в первом разряде
	LCD->RAM[0] = BIT(28);
	LCD->RAM[2] = BIT(1);

	// запрос вывода новых данных
	LCD->SR |= LCD_SR_UDR;

	while (1)
	{
	}

	return 0;
}

Пример базируются на проекте приведенном ранее:

2011-08-15-STM32L-GPIO – [74.52 kB] [ Версия 2011-08-15 ]

с исправлениями в макросах для работы с линиями ввода-вывода:

http://ziblog.ru/2011/12/07/stm32l-linii-vvoda-vyivoda-chast-2/

Документация на микроконтроллер:

DS6876: Справочный листок (Основные характеристики микроконтроллера)

RM0038: STM32L151xx and STM32L152xx advanced ARM-based 32-bit MCUs (Руководство пользователя)

STM32L151xx and STM32L152xx ultralow power limitations (Известные на данный момент ошибки)

Полный комплект документации доступен на сайте производителя: перейти.


Документация по плате:

UM1079: STM32L-DISCOVERY (Руководство пользователя)

Board schematics for STM32L-DISCOVERY board (Принципиальная схема)


О различиях между L1, F1 и F2 можно прочитать в документе:

AN3364: Migration and compatibility guidelines for STM32 microcontroller applications.



comments powered by Disqus