Янв 072012
 

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

Установленный на плате чип STM32L152RBT6 имеет собственный контроллер для управления жидкокристаллическим дисплеем. Поначалу я не мог определиться, каким путем пойти, а было два варианта:
первый — использовать готовые библиотеки из демонстрационных примеров, и через их функции пытаться "запустить" LCD;
второй — никаких библиотек, все настраиваем вручную под конкретный тип LCD, разбираемся со всеми регистрами, битами, схемой подключения и т.п.
Остановился на втором варианте. Готовая библиотека — это, конечно, хорошо, но написана она именно для этого дисплея (тут я имею в виду библиотеку из демонстрационного ПО для данной платы). Для другого индикатора уже не подойдет. Да и вникать в готовые библиотечные функции проще, когда уже понимаешь назначение регистров и имеешь в голове какой-то алгоритм действий.
Итак, начнем с дисплея. Под стеклом он имеет такое расположение пикселей.

Карта сегментов LCD
Рисунок 1

Содержит 6 полей для отображения букв и цифр, по 14 сегментов в каждом поле. Плюс к тому 4 точки (dp), 4 двоеточия (colon) и 4 полоски (bar).
Весь этот набор сегментов разделен на 4 группы по 24 сегмента в каждой. Ну и соответственно, каждая группа имеет свой отдельный "общий провод": COM0 — COM3. В общем, стандартная динамическая индикация. В ОЗУ выделены специальные ячейки RAM[x], информация в которых соответствует каждой из этих четырех групп сегментов. Ну а чтобы вся информация шла к индикатору без какого либо программного вывода, соответствующие линии портов настраиваются на альтернативную функцию работы с памятью LCD-контроллера. Тогда контроллер управляет дисплеем аппаратно. Какие-то действия от программы нужны только когда требуется обновить информацию на дисплее. Ниже на рисунке видно все 4 группы сегментов, соответствующие им управляющие линии COM, а также области "видеопамяти".

COM 0-4
Рисунок 2

Не удивляйтесь тому факту, что для каждой группы из 24 сегментов индикатора в памяти выделено по два 32-разрядных слова RAM[x]. Это в данном индикаторе 4 группы COM0..3 по 24 сегмента в каждой, то есть 24х4. А максимальные возможности контроллера поболее: он может управлять 176 сегментами (4 управляющих линии COM0..3, на 44 сегмента каждая) или даже 320 сегментами (8 управляющих линий COM0..7 на 40 сегментов каждая). Поэтому вот такую структуру имеет "видеопамять" контроллера:

RAM_SEG0_43
Таблица 1

Здесь показаны области памяти RAM[0] (сегменты SEG0..31 для COM0) и RAM[1] (сегменты SEG32..43 также для COM0). Такой же вид они имеют и для следущих трех групп COM1..3, там также по 44 сегмента. А вот следующие 8 ячеек памяти позволяют управлять уже 40 сегментами по 4 линиям COM4..7

RAM_SEG0_39
Таблица 2

Дополнительно есть функция так называемого "ремаппинга". Так перераспределяется порядок вывода некоторых разрядов. Рассматривать сильно подробно не буду, там не один вариант, скажу лишь что индикатор на плате подключен не очень удачно, задействованы биты "видеопамяти" SEG40, SEG41. То есть одним 32 разрядным значением RAM[0] все сегменты линии COM0, например, не включишь, придется что-то писать и в RAM[1], именно там у нас разряды SEG40, SEG41. Вот тут и пригодится "ремаппинг". Как увидим далее, разряды SEG31..SEG28 "видеопамяти" у нас для вывода информации именно на данный дисплей не задействованы. Поэтому, то что нужно записать в разряды SEG43..SEG40 будем смело писать в другие — SEG31..SEG28. А дальше мультиплексор автоматически перенаправит их вместо SEG43..SEG40. Выигрыш тут в том, что выполняем одну операцию записи в память RAM[0] вместо двух RAM[0]:RAM[1] для вывода, например, в группу COM0. А для вывода в COM3..COM0 уже 4 операции записи вместо 8-ми.
В руководстве пользователя на плату STM32L-DISCOVERY (User Manual UM1079 от STMicroelectronics) описан следующий метод управления индикатором:
Мультиплексируемый вывод информации с 1/4 duty и 1/3 bias.
Теперь разберемся подробней с этим методом. Изначально на встроенный контроллер ЖКИ поступает тактовая частота LCDCLK.
Как уже упоминалось в статье STM32L. Система формирования тактовых частот Reset and Clock Control (RCC), источниками тактовой частоты могут быть:
Внешний НЧ генератор с кварцем на 32,768 кГц — LSE
Внутренний НЧ генератор на 37 кГц — LSI
Внешний ВЧ генератор HSE, при этом его частота не более 1 МГц.
Далее входная частота делится, коэффициенты деления устанавливаются битами PS[3:0] и DIV[3:0] регистра LCD_FCR (Frame Control Register). Формула пересчета такова:

FCKDIV = FLCDCLK / (2PS x (16+DIV))

и далее

FFRAME = FCKDIV x duty

Теперь пояснения.
Что такое Frame? Тут можно провести аналогию с телевизионным "кадром", за время одного "фрейма" на дисплей последовательно выводится информация из ячеек памяти RAM[x], RAM[x+1] и т.д. Поочередно на все управляющие линии COM и соотвествующие им сегменты. Для нашего индикатора за один "фрейм" нужно вывести информацию на четыре группы сегментов, от COM0 до COM3. То есть, длительность управляющего импульса для COM0 (и для остальных трех) должна быть равна 1/4 длительности "фрейма". Вот это и есть длительность duty = 1/4 frame. Duty можно перевести как "коэффициент заполнения" — отношение длительности импульса к его периоду. Вот как изображена форма сигналов в Refernce manual для нашего случая

Frame
Рисунок 3

Что такое Bias? Тот же Reference Manual трактует этот термин так — это количество уровней напряжения, используемого для управления ЖКИ. Определяется как 1/(количество уровней — 1). То есть, в нашем случае, имеется 4 уровня напряжения для управления линиями COM и сегментами SEG индикатора (см. предыдущий рисунок). Поскольку источник питания LCD у нас положительной полярности, меняя уровни напряжения на SEG и COM, мы можем получить как положительное, так и отрицательное значение напряжения между ними. Обратите внимание, что полярность импульсов меняется в следующем "фрейме" — на "жидкие кристаллы" надо подавать переменное управляющее напряжение с чередующейся полярностью, постоянное смещение ведет к их деградации.

COM - SEG
Рисунок 4

Значения Duty и Bias устанавливаются через регистр LCD_CR (LCD Control Register), в разрядах BIAS[1:0] и DUTY[2:0].

В Reference Manual на стр.246-247 есть таблица с примерами расчета частоты "фреймов" в зависимости от установленных коэффициентов деления PS[3:0] и DIV[3:0] регистра LCD_FCR (Frame Control Register), а также коэффициента Duty и входной частоты FLCDCLK. Тут я выбрал первое из подходящих значений для duty = 1/4

Выбор Fframe
Таблица 3

Пора перейти к практической части — написанию кода. Как создать и настроить проект в IAR EWARM, сконфигурировать порты можно почитать тут
Создание проекта в IAR EWARM. Работа с портами ввода/вывода. Часть 1.
и тут
Создание проекта в IAR EWARM. Работа с портами ввода/вывода. Часть 2.

Итак, для начала в файле main.c подключим внешний заголовочный файл stm32l1xx.h

#include "stm32l1xx.h"

Он нам нужен для выполнения всех настроек портов ввода/вывода и LCD контроллера.

Далее пишем такие строки

#define GPIO_AFRL_AF11(x) (0x0B<<(x*4))
#define GPIO_AFRH_AF11(x) (0x0B<<((x-8)*4))

void gpio_init(void);
void lcd_init(void);

Про макроопределения #define разъяснение будет чуть позже, а за ними следуют два объявления функций gpio_init и lcd_init. Это функции конфигурации задействованных линий портов ввода/вывода и контроллера LCD соответственно. Далее, мы их поочередно вызываем в начале главной функции программы

int main()
{
gpio_init();
lcd_init();
…………..

Функция конфигурации портов ввода/вывода — gpio_init().
Для управления дисплеем соответствующие линии портов должны быть настроены следующим образом:
Режимы работы — выход, push-pull, без подтягивающих резисторов, частота вывода в порт 400 кГц (тут на выбор), альтернативная функция AF11.
Разводка печатной платы устанавливает такое вот соответствие между выводами ЖК индикатора (LCDSEGx, COMx) и выводами микроконтроллера.

Подключение LCD
Таблица 4

Разводка такова, что номера большинства сегментов ЖКИ (LSDSEGx) не соответствуют номерам сегментов "видеопамяти" микроконтроллера (SEGx). В части LCD расположение групп можно наглядно посмотреть на Рисунке 2.
Соответствующие выводы портов микроконтроллера (Таблица 4) необходимо сконфигурировать на работу с альтернативной функцией AF11, тогда значения ячеек "видеопамяти" RAM[x] и управляющие сигналы COMx будут напрямую выводится в нужные линии портов.
Рассмотрим подробней код конфигурации портов.
Вначале разрешаем тактирование портов от матрицы шин AHB

RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN);

Затем настраиваем соответствующие линии в режим выхода:

GPIOA->MODER |= (GPIO_MODER_MODER1_1|GPIO_MODER_MODER2_1|GPIO_MODER_MODER3_1
|GPIO_MODER_MODER8_1|GPIO_MODER_MODER9_1|GPIO_MODER_MODER10_1
|GPIO_MODER_MODER15_1);

GPIOB->MODER |= (GPIO_MODER_MODER3_1|GPIO_MODER_MODER4_1|GPIO_MODER_MODER5_1
|GPIO_MODER_MODER8_1|GPIO_MODER_MODER9_1|GPIO_MODER_MODER10_1
|GPIO_MODER_MODER11_1|GPIO_MODER_MODER12_1|GPIO_MODER_MODER13_1
|GPIO_MODER_MODER14_1|GPIO_MODER_MODER15_1);

GPIOC->MODER |= (GPIO_MODER_MODER0_1|GPIO_MODER_MODER1_1|GPIO_MODER_MODER2_1
|GPIO_MODER_MODER3_1|GPIO_MODER_MODER6_1|GPIO_MODER_MODER7_1
|GPIO_MODER_MODER8_1|GPIO_MODER_MODER9_1|GPIO_MODER_MODER10_1
|GPIO_MODER_MODER11_1);

Здесь раскрыл подробно устанавливаемые биты для каждой задействованной линии порта. Потом можно будет сократить для уменьшения текста.
Далее — режим push-pull для тех же выводов:

GPIOA->OTYPER &= ~(GPIO_OTYPER_OT_1|GPIO_OTYPER_OT_2|GPIO_OTYPER_OT_3
|GPIO_OTYPER_OT_8|GPIO_OTYPER_OT_9|GPIO_OTYPER_OT_10
|GPIO_OTYPER_OT_15);

GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_3|GPIO_OTYPER_OT_4|GPIO_OTYPER_OT_5
|GPIO_OTYPER_OT_8|GPIO_OTYPER_OT_9|GPIO_OTYPER_OT_10
|GPIO_OTYPER_OT_11|GPIO_OTYPER_OT_12|GPIO_OTYPER_OT_13
|GPIO_OTYPER_OT_14|GPIO_OTYPER_OT_15);

GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_0|GPIO_OTYPER_OT_1|GPIO_OTYPER_OT_2
|GPIO_OTYPER_OT_3|GPIO_OTYPER_OT_6|GPIO_OTYPER_OT_7
|GPIO_OTYPER_OT_8|GPIO_OTYPER_OT_9|GPIO_OTYPER_OT_10
|GPIO_OTYPER_OT_11);

Отключаем подтягивающие резисторы:

GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR1|GPIO_PUPDR_PUPDR2|GPIO_PUPDR_PUPDR3
|GPIO_PUPDR_PUPDR8|GPIO_PUPDR_PUPDR9|GPIO_PUPDR_PUPDR10
|GPIO_PUPDR_PUPDR15);

GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR3|GPIO_PUPDR_PUPDR4|GPIO_PUPDR_PUPDR5
|GPIO_PUPDR_PUPDR8|GPIO_PUPDR_PUPDR9|GPIO_PUPDR_PUPDR10
|GPIO_PUPDR_PUPDR11|GPIO_PUPDR_PUPDR12|GPIO_PUPDR_PUPDR13
|GPIO_PUPDR_PUPDR14|GPIO_PUPDR_PUPDR15);

GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPDR0|GPIO_PUPDR_PUPDR1|GPIO_PUPDR_PUPDR2
|GPIO_PUPDR_PUPDR3|GPIO_PUPDR_PUPDR6|GPIO_PUPDR_PUPDR7
|GPIO_PUPDR_PUPDR8|GPIO_PUPDR_PUPDR9|GPIO_PUPDR_PUPDR10
|GPIO_PUPDR_PUPDR11);

Задаем частоту вывода в порты — 400 кГц:

GPIOA->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR1|GPIO_OSPEEDER_OSPEEDR2
|GPIO_OSPEEDER_OSPEEDR3|GPIO_OSPEEDER_OSPEEDR8
|GPIO_OSPEEDER_OSPEEDR9|GPIO_OSPEEDER_OSPEEDR10
|GPIO_OSPEEDER_OSPEEDR15);

GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR3|GPIO_OSPEEDER_OSPEEDR4
|GPIO_OSPEEDER_OSPEEDR5|GPIO_OSPEEDER_OSPEEDR8
|GPIO_OSPEEDER_OSPEEDR9|GPIO_OSPEEDER_OSPEEDR10
|GPIO_OSPEEDER_OSPEEDR11|GPIO_OSPEEDER_OSPEEDR12
|GPIO_OSPEEDER_OSPEEDR13|GPIO_OSPEEDER_OSPEEDR14
|GPIO_OSPEEDER_OSPEEDR15);

GPIOC->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0|GPIO_OSPEEDER_OSPEEDR1
|GPIO_OSPEEDER_OSPEEDR2|GPIO_OSPEEDER_OSPEEDR3
|GPIO_OSPEEDER_OSPEEDR6|GPIO_OSPEEDER_OSPEEDR7
|GPIO_OSPEEDER_OSPEEDR8|GPIO_OSPEEDER_OSPEEDR9
|GPIO_OSPEEDER_OSPEEDR10|GPIO_OSPEEDER_OSPEEDR11);

И, наконец, настраиваем эти выводы на работу с LCD, задавая для них альтернативную функцию AF11.

GPIOA->AFR[0] |= (GPIO_AFRL_AF11(1)|GPIO_AFRL_AF11(2)|GPIO_AFRL_AF11(3));
GPIOA->AFR[1] |= (GPIO_AFRH_AF11(8)|GPIO_AFRH_AF11(9)|GPIO_AFRH_AF11(10)
|GPIO_AFRH_AF11(15));

GPIOB->AFR[0] |= (GPIO_AFRL_AF11(3)|GPIO_AFRL_AF11(4)|GPIO_AFRL_AF11(5));
GPIOB->AFR[1] |= (GPIO_AFRH_AF11(8)|GPIO_AFRH_AF11(9)|GPIO_AFRH_AF11(10)
|GPIO_AFRH_AF11(11)|GPIO_AFRH_AF11(12)|GPIO_AFRH_AF11(13)
|GPIO_AFRH_AF11(14)|GPIO_AFRH_AF11(15));

GPIOC->AFR[0] |= (GPIO_AFRL_AF11(0)|GPIO_AFRL_AF11(1)|GPIO_AFRL_AF11(2)
|GPIO_AFRL_AF11(3)|GPIO_AFRL_AF11(6)|GPIO_AFRL_AF11(7));
GPIOC->AFR[1] |= (GPIO_AFRH_AF11(8)|GPIO_AFRH_AF11(9)|GPIO_AFRH_AF11(10)
|GPIO_AFRH_AF11(11));

Вот для последней операции и нужны были два макроопределения в начале программы:

#define GPIO_AFRL_AF11(x) (0x0B<<(x*4))
#define GPIO_AFRH_AF11(x) (0x0B<<((x-8)*4))

Дело в том, что настройка альтернативных функций для каждого порта производится через два 32-разрядных регистра AFRL и AFRH. Один регистр настраивает младшие выводы порта, другой — старшие. Для каждой линии в регистрах выделено по 4 бита, то есть до 16 вариантов выбора альтернативных функций. А поскольку весь код установки/сброса нужных бит я решил привести в более наглядном виде с номерами соответствующих линий портов, пришлось допольнительно задать некие формулы для сдвига в нужные разряды.
Теперь можно сократить весь предыдущий код, заменив все длинные списки значений после знаков равенства на соответствующие шестнадцатеричные числа. Тогда код будет выглядеть так:

void gpio_init(void)
 {
  RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN);
  GPIOA->MODER |= 0x802A00A8;
  GPIOB->MODER |= 0xAAAA0A80;
  GPIOC->MODER |= 0x00AAA0AA;
  GPIOA->OTYPER &= ~0x0000870E;
  GPIOB->OTYPER &= ~0x0000FF38;
  GPIOC->OTYPER &= ~0x00000FCF;
  GPIOA->PUPDR &= ~0xC03F00FC;
  GPIOB->PUPDR &= ~0xFFFF0FC0;
  GPIOC->PUPDR &= ~0x00FFF0FF;
  GPIOA->OSPEEDR &= ~0xC03F00FC;
  GPIOB->OSPEEDR &= ~0xFFFF0FC0;
  GPIOC->OSPEEDR &= ~0xFFFFF0FF;
  GPIOA->AFR[0] |= 0x0000BBB0;
  GPIOA->AFR[1] |= 0xB0000BBB;
  GPIOB->AFR[0] |= 0x00BBB000;
  GPIOB->AFR[1] |= 0xBBBBBBBB;
  GPIOC->AFR[0] |= 0xBB00BBBB;
  GPIOC->AFR[1] |= 0x0000BBBB;
 }

Теперь и макроопределения #define в начале программы можно смело выкинуть из кода. Как уже упоминал, такой длинный текст функции я расписал для наглядности, чтобы было понятно какими битами регистров настраиваем порты. Однако, не советую применять такие методы при написании программ, код, конечно, сокращается значительно, но на следующий день вы сами в нем не разберетесь!

Функция инициализации контроллера LCD — lcd_init().

Сначала разрешаем тактирование модулей управления питанием и LCD, иначе работать с регистрами будет невозможно!

RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_LCDEN);

В регистре управления питанием PWR_CR снимаем защиту от записи для регистра RCC_CSR. Этот регистр как раз и управляет источниками тактирования часов RTC и модуля LCD

PWR->CR |= PWR_CR_DBP;

Для смены источника тактирования модуля LCD (и часов RTC тоже) необходимо сначала выполнить сброс источника тактирования установкой бита RTCRST в регистре RCC_CSR.

RCC->CSR |= RCC_CSR_RTCRST;

А затем сбросить этот бит для выбора нового источника.

RCC->CSR &= ~RCC_CSR_RTCRST;

Включаем внешний генератор с кварцем на 32,768 кГц

RCC->CSR |= RCC_CSR_LSEON;

Ждем стабилизации генератора

while(!(RCC->CSR & RCC_CSR_LSERDY));

Выбираем внешний генератор LSE для тактирования модуля LCD

RCC->CSR |= RCC_CSR_RTCSEL_LSE;

Далее устанавливаем нужный режим BIAS — количество уровней управляющих напряжений.

Для начала сбрасываем все биты, чтобы не осталось "мусора".

LCD->CR &= ~LCD_CR_BIAS;

А затем устанавливаем нужные биты для выбора режима "1/3 bias" — 4 уровня управляющих напряжений.

LCD->CR |= LCD_CR_BIAS_1;

Устанавливаем режим "1/4 duty". Для этого также вначале сбрасываем все биты

LCD->CR &=~LCD_CR_DUTY;

Затем устанавливаем нужные.

LCD->CR |= (LCD_CR_DUTY_0 | LCD_CR_DUTY_1);

Делаем "ремаппинг" выводов. При этом на выводы определенные под разряды памяти SEG43..40 будут выводиться значения из SEG31..28. Этим экономим количество операций записи в "видеопамять", теперь будем работать только с ячейками RAM[0], RAM[2], RAM[4], RAM[6].

LCD->CR |= LCD_CR_MUX_SEG;

Устанавливаем предделители. Вначале также очищаем все биты, затем устанавливаем нужные. Битами предделителя PS[3:0] выставляем ck_ps = LCDCLK/16.

LCD->FCR &= ~LCD_FCR_PS;

LCD->FCR |= (1<<24);

Далее битами DIV[3:0] выставляем ck_div = ck_ps/17.

LCD->FCR&= ~LCD_FCR_DIV;

LCD->FCR |= (1<<18);

Напомню, что этими коэффициентами делим входную частоту LCDCLK = 32,768 кГц до частоты "фрейма" 30,12 Гц.

Устанавливаем нужный уровень контраста. Выбираем среднюю контрастность.

LCD->FCR &= ~LCD_FCR_CC;

LCD->FCR |= LCD_FCR_CC_1;

Ждем синхронизации регистра LCD_FCR

while(!(LCD->SR & LCD_SR_FCRSR));

Выбираем внутренний step-up converter для формирования питания Vlcd

LCD->CR &= ~LCD_CR_VSEL;

Разрешаем работу LCD-контроллера

LCD->CR |= LCD_CR_LCDEN;

Ждем готовности внутреннего step-up converter

while(!(LCD->SR & LCD_SR_RDY));

Ждем готовности LCD-контроллера

while(!(LCD->SR & LCD_SR_ENS));

На этом функция инициализации контроллера LCD завершается и происходит возврат в основную функцию main().

После функций инициализации портов и LCD начинаем выводить данные на индикатор. Вначале необходимо проверить завершена ли предыдущая передача данных. Для этого проверяем бит UDR (Update Display Request) регистра LCD_SR, и ждем его очистки.

while(LCD->SR & LCD_SR_UDR);

Затем пишем значения в ячейки "видеопамяти".

LCD->RAM[0] = 0x02200B00;

LCD->RAM[2] = 0x07340D00;

LCD->RAM[4] = 0x00001004;

LCD->RAM[6] = 0x04040000;

А теперь снова устанавливаем бит UDR в регистре LCD_SR, инициируя обновление данных для дисплея (пересылку новых данных из памяти).

LCD->SR |= LCD_SR_UDR;

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

Ну а далее — бесконечный цикл

while (1);

поскольку никаких действий в программе нам уже не нужно, а контроллер управляет дисплеем аппаратно.

По ссылке ниже можно скачать архив с проектом

Архив с проектом

Вот и все, программу можно загружать и запускать.

Результат

 

Другие статьи:

  24 Responses to “STM32L-DISCOVERY. Подключаем LCD.”

  1. Написал функции вывода символа, вывода строки и вывода бегущей строки. Мож кому интересно, как сдесь выложить?

  2. Здесь только текстом. Файлы можно на форуме выложить.

  3. виснет на этой строчке

    Ждем готовности внутреннего step-up converter
    while(!(LCD->SR & LCD_SR_RDY));

    на другом чипе работает. что может быть? отличие — нет внешнего кварца.

    • То есть отключение кварца приводит к такому результату? Так сразу причину и не скажу, не сталкивался. Надо рыть документацию.

    • У меня похожая ситуёвина, вчера «тест» светился а севодня включил и нет реакции программа доходит до первого while в функции lcd_init и вешается такое впечетление что LCD не реагирует!!!

  4. на плате где кварц, если его вынуть, запускается через пару секунд. а на другой виснет на том месте.
    по какой причине может там виснуть? кварц 32768 генерит. напряжение на 1 ноге есть.

  5. Написал небольшую программу для генерацию значений в регистре RAM.
    В архиве exe, исходники и проект для VS 2010

    https://dl.dropboxusercontent.com/u/64727368/stm32l1xx_lcd_CodeGen.zip

  6. Не понятно, как определить соответствие между конкретным пикселем и конкретным битом в ячейке RAM?

  7. Если не затруднит, напишите, пожалуйста, из каких документов брали таблицы и рисунки?

  8. Будьте добры), подскажите. Где можно найти информацию со второго рисунка?

  9. Будьте добры, скажите, Вы таблицу 4, в этой статье, сами делали? Или просто вырезали из User Manual^а на МК? Потому что у меня там чуть другая таблица в которой не хватает колонки STM32L152RBT6.

    • Разобрался.
      Вдруг кому поможет.
      Всё дело в соответствии входов LCD и выводов самого микроконтроллера(не платы! а микрухи stm32l152rbt6).
      Нужно будет два документа что бы установить это соответствие.
      User manual на плату stm32l-disovery(стр. 27, Table 7. LCD connections)
      и
      Datasheet на МК stm32l152rbt6(стр. 36, Table 8. STM32L15xxx pin definitions ).
      Мир!