[Программирование микроконтроллеров] Подключение OLED дисплея ssd1306 к STM32 (SPI+DMA)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В данной статье будет описан процесс подключение oled дисплея с контроллером ssd1306 разрешением 128x64 к микроконтроллеру stm32f103C8T6 по интерфейсу SPI. Также мне хотелось добиться максимальной скорости обновления дисплея, поэтому целесообразно использовать DMA, а программирование микроконтроллера производить с помощью библиотеки CMSIS.
Подключение
Подключать дисплей к микроконтроллеру будем по интерфейсу SPI1 по следующей схеме:
- VDD-> +3.3В
- GND-> Земля
- SCK -> PA5
- SDA -> PA7(MOSI)
- RES-> PA1
- CS-> PA2
- DS-> PA3
Передача данных происходит по возрастающему фронту сигнала синхронизации по 1 байту за кадр. Линии SCK и SDA служат для передачи данных по интерфейсу SPI, RES — перезагружает контроллер дисплея при низком логическом уровне, CS отвечает за выбор устройства на шине SPI при низком логическом уровне, DS определяет тип данных (команда — 1/данные — 0) которые передаются дисплею. Так как с дисплея ничего считать нельзя, вывод MISO использовать не будем.
Организация памяти контроллера дисплея
Перед тем, как выводить что-либо на экран, необходимо разобраться как в контроллере ssd1306 организована память.
Вся графическая память (GDDRAM) представляет собой область 128*64=8192 бит=1 Кбайт. Область разбита на 8 страниц, которые представлены в виде в виде совокупности из 128-ми 8-ми битных сегментов. Адресация памяти происходит по номеру страницы и номеру сегмента соответственно.
При таком методе адресации есть очень неприятная особенность — невозможность записать в память 1 бит информации, так как запись происходит по сегменту (по 8 бит). А так как для корректного отображения единичного пикселя на экране, необходимо знать состояние остальных пикселей в сегменте, целесообразно создать в памяти микроконтроллера буфер размером 1 Кбайт и циклически загружать его в память дисплея (тут и пригодится DMA), соответственно, производя его полное обновление. При использовании такого метода возможно пересчитать положение каждого бита в памяти на классические координаты x,y. Тогда для вывода на экран точки с координатами x и y воспользуемся следующим способом:
displayBuff[x+(y/8)*SSD1306_WIDTH]|=(1<<(y%8));
А для того, чтобы стереть точку
displayBuff[x+(y/8)*SSD1306_WIDTH]&=~(1<<(y%8));
Настройка SPI
Как говорилось выше, подключать дисплей будем к SPI1 микроконтроллера STM32F103C8.
Для удобства написания кода объявим некоторые константы и создадим функцию для инициализации SPI.
#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
#define BUFFER_SIZE 1024
//Макросы для активации устройства на шине, сброса экрана и выбора команды/данных
#define CS_SET GPIOA->BSRR|=GPIO_BSRR_BS2
#define CS_RES GPIOA->BSRR|=GPIO_BSRR_BR2
#define RESET_SET GPIOA->BSRR|=GPIO_BSRR_BS1
#define RESET_RES GPIOA->BSRR|=GPIO_BSRR_BR1
#define DATA GPIOA->BSRR|=GPIO_BSRR_BS3
#define COMMAND GPIOA->BSRR|=GPIO_BSRR_BR3
void spi1Init()
{
return;
}
Включим тактирование и произведем настройку выходов GPIO, как показано в таблице выше.
RCC->APB2ENR|=RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN;//Включить тактирование SPI1 и GPIOA
RCC->AHBENR|=RCC_AHBENR_DMA1EN;//Включить тактирование DMA
GPIOA->CRL|= GPIO_CRL_MODE5 | GPIO_CRL_MODE7;//PA4,PA5,PA7 в режим выходов 50MHz
GPIOA->CRL&= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF7);
GPIOA->CRL|= GPIO_CRL_CNF5_1 | GPIO_CRL_CNF7_1;//PA5,PA7 - выход с альтернативной функцией push-pull, PA4 - выход push-pull
Далее произведем настройку SPI в режим master и частотой 18 Мгц.
SPI1->CR1|=SPI_CR1_MSTR;//Режим ведущего
SPI1->CR1|= (0x00 & SPI_CR1_BR);//Делитель частоты на 2
SPI1->CR1|=SPI_CR1_SSM;//Программный NSS
SPI1->CR1|=SPI_CR1_SSI;//NSS - high
SPI1->CR2|=SPI_CR2_TXDMAEN;//Разрешить запросы DMA
SPI1->CR1|=SPI_CR1_SPE;//включить SPI1
Настроим DMA.
DMA1_Channel3->CCR|=DMA_CCR1_PSIZE_0;//Размер периферии 1байт
DMA1_Channel3->CCR|=DMA_CCR1_DIR;//Режим DMA из памяти в периферию
DMA1_Channel3->CCR|=DMA_CCR1_MINC;//Включить инкремент памяти
DMA1_Channel3->CCR|=DMA_CCR1_PL;//Высокий приоритет DMA
Далее напишем функцию отправки данных по SPI (пока без DMA). Процесс обмена данными заключается в следующем:
- Ожидаем, пока SPI освободится
- CS=0
- Отправка данных
- CS=1
void spiTransmit(uint8_t data)
{
CS_RES;
SPI1->DR = data;
while((SPI1->SR & SPI_SR_BSY))
{};
CS_SET;
}
Также напишем функцию непосредственно отправки команды экрану (Переключение линии DC производим только при передаче команды, а затем возвращаем ее в состояние «данные», так как команды передавать будем не так часто и в производительности не потеряем).
void ssd1306SendCommand(uint8_t command)
{
COMMAND;
spiTransmit(command);
DATA;
}
Далее займемся функциями для работы непосредственно с DMA, для этого объявим буфер в памяти микроконтроллера и создадим функции для начала и остановки циклической отправки этого буфера в память экрана.
static uint8_t displayBuff[BUFFER_SIZE];//Буфер экрана
void ssd1306RunDisplayUPD()
{
DATA;
DMA1_Channel3->CCR&=~(DMA_CCR1_EN);//Выключить DMA
DMA1_Channel3->CPAR=(uint32_t)(&SPI1->DR);//Занесем в DMA адрес регистра данных SPI1
DMA1_Channel3->CMAR=(uint32_t)&displayBuff;//Адрес данных
DMA1_Channel3->CNDTR=sizeof(displayBuff);//Размер данных
DMA1->IFCR&=~(DMA_IFCR_CGIF3);
CS_RES;//Выбор устройства на шине
DMA1_Channel3->CCR|=DMA_CCR1_CIRC;//Циклический режим DMA
DMA1_Channel3->CCR|=DMA_CCR1_EN;//Включить DMA
}
void ssd1306StopDispayUPD()
{
CS_SET;//Дезактивация устройства на шине
DMA1_Channel3->CCR&=~(DMA_CCR1_EN);//Выключить DMA
DMA1_Channel3->CCR&=~DMA_CCR1_CIRC;//Выключить циклический режим
}
Инициализация экрана и вывод данных
Теперь создадим функцию для инициализации самого экрана.
void ssd1306Init()
{
}
Для начала настроим CS, RESET и линию DC, а также произведем сброс контроллера дисплея.
uint16_t i;
GPIOA->CRL|= GPIO_CRL_MODE2 |GPIO_CRL_MODE1 | GPIO_CRL_MODE3;
GPIOA->CRL&= ~(GPIO_CRL_CNF1 | GPIO_CRL_CNF2 | GPIO_CRL_CNF3);//PA1,PA2,PA3 в режим выхода
//Сброс экрана и очистка буфера
RESET_RES;
for(i=0;i<BUFFER_SIZE;i++)
{
displayBuff[i]=0;
}
RESET_SET;
CS_SET;//Выбор устройства на шине
Далее отправим последовательность команд для инициализации (Более подробно о них можно узнать в документации на контроллер ssd1306).
ssd1306SendCommand(0xAE); //display off
ssd1306SendCommand(0xD5); //Set Memory Addressing Mode
ssd1306SendCommand(0x80); //00,Horizontal Addressing Mode;01,Vertical
ssd1306SendCommand(0xA8); //Set Page Start Address for Page Addressing
ssd1306SendCommand(0x3F); //Set COM Output Scan Direction
ssd1306SendCommand(0xD3); //set low column address
ssd1306SendCommand(0x00); //set high column address
ssd1306SendCommand(0x40); //set start line address
ssd1306SendCommand(0x8D); //set contrast control register
ssd1306SendCommand(0x14);
ssd1306SendCommand(0x20); //set segment re-map 0 to 127
ssd1306SendCommand(0x00); //set normal display
ssd1306SendCommand(0xA1); //set multiplex ratio(1 to 64)
ssd1306SendCommand(0xC8); //
ssd1306SendCommand(0xDA); //0xa4,Output follows RAM
ssd1306SendCommand(0x12); //set display offset
ssd1306SendCommand(0x81); //not offset
ssd1306SendCommand(0x8F); //set display clock divide ratio/oscillator frequency
ssd1306SendCommand(0xD9); //set divide ratio
ssd1306SendCommand(0xF1); //set pre-charge period
ssd1306SendCommand(0xDB);
ssd1306SendCommand(0x40); //set com pins hardware configuration
ssd1306SendCommand(0xA4);
ssd1306SendCommand(0xA6); //set vcomh
ssd1306SendCommand(0xAF); //0x20,0.77xVcc
Создадим функции для заполнения всего экрана выбранным цветом и отображения одного пикселя.
typedef enum COLOR
{
BLACK,
WHITE
}COLOR;
void ssd1306DrawPixel(uint16_t x, uint16_t y,COLOR color){
if(x<SSD1306_WIDTH && y <SSD1306_HEIGHT && x>=0 && y>=0)
{
if(color==WHITE)
{
displayBuff[x+(y/8)*SSD1306_WIDTH]|=(1<<(y%8));
}
else if(color==BLACK)
{
displayBuff[x+(y/8)*SSD1306_WIDTH]&=~(1<<(y%8));
}
}
}
void ssd1306FillDisplay(COLOR color)
{
uint16_t i;
for(i=0;i<SSD1306_HEIGHT*SSD1306_WIDTH;i++)
{
if(color==WHITE)
displayBuff[i]=0xFF;
else if(color==BLACK)
displayBuff[i]=0;
}
}
Далее в теле основной программы инициализируем SPI и дисплей.
RccClockInit();
spi1Init();
ssd1306Init();
Функция RccClockInit() предназначена для настройки тактирования микроконтроллера.
RccClockInit код
SPL
int RccClockInit()
{
//Enable HSE
//Setting PLL
//Enable PLL
//Setting count wait cycles of FLASH
//Setting AHB1,AHB2 prescaler
//Switch to PLL
uint16_t timeDelay;
RCC->CR|=RCC_CR_HSEON;//Enable HSE
for(timeDelay=0;;timeDelay++)
{
if(RCC->CR&RCC_CR_HSERDY) break;
if(timeDelay>0x1000)
{
RCC->CR&=~RCC_CR_HSEON;
return 1;
}
}
RCC->CFGR|=RCC_CFGR_PLLMULL9;//PLL x9
RCC->CFGR|=RCC_CFGR_PLLSRC_HSE;//PLL sourse:HSE
RCC->CR|=RCC_CR_PLLON;//Enable PLL
for(timeDelay=0;;timeDelay++)
{
if(RCC->CR&RCC_CR_PLLRDY) break;
if(timeDelay>0x1000)
{
RCC->CR&=~RCC_CR_HSEON;
RCC->CR&=~RCC_CR_PLLON;
return 2;
}
}
FLASH->ACR|=FLASH_ACR_LATENCY_2;
RCC->CFGR|=RCC_CFGR_PPRE1_DIV2;//APB1 prescaler=2
RCC->CFGR|=RCC_CFGR_SW_PLL;//Switch to PLL
while((RCC->CFGR&RCC_CFGR_SWS)!=(0x02<<2)){}
RCC->CR&=~RCC_CR_HSION;//Disable HSI
return 0;
}
Зальем весь дисплей белым цветом и посмотрим результат.
ssd1306RunDisplayUPD();
ssd1306FillDisplay(WHITE);
Нарисуем на экране в сетку шагом в 10 пикселей.
for(i=0;i<SSD1306_WIDTH;i++)
{
for(j=0;j<SSD1306_HEIGHT;j++)
{
if(j%10==0 || i%10==0)
ssd1306DrawPixel(i,j,WHITE);
}
}
Функции работают корректно, буфер непрерывно записывается в память контроллера дисплея, что позволяет при отображении графических примитивов пользоваться декартовой системой координат.
Частота обновления дисплея
Так как буфер отправляется в память дисплея циклически, для приблизительного определения частоты обновления дисплея достаточно будет узнать время, за которое DMA осуществляет полную передачу данных. Для отладки в реальном времени воспользуемся библиотекой EventRecorder из Keil.
Для того, чтобы узнать момент окончания передачи данных, настроим прерывание DMA на окончание передачи.
DMA1_Channel3->CCR|=DMA_CCR1_TCIE;//Прерывание по завершении передачи
DMA1->IFCR&=~DMA_IFCR_CTCIF3;//Сбрасываем флаг прерывания
NVIC_EnableIRQ(DMA1_Channel3_IRQn);//Включить прерывание
Промежуток времени будем отслеживать с помощью функций EventStart и EventStop.
Получаем 0.00400881-0.00377114=0.00012767 сек, что соответствует частоте обновления 4.2 Кгц. На самом деле частота не такая большая, что связано с неточностью способа измерения, но явно больше стандартных 60 Гц.
Ссылки
===========
Источник:
habr.com
===========
Похожие новости:
- [Космонавтика, DIY или Сделай сам] Целимся и общаемся со спутниками: Часть первая — целимся программно
- [Программирование микроконтроллеров, Схемотехника, Производство и разработка электроники] Как я на спор в разработчики электроники попал
- [Гаджеты, Компьютерное железо, Периферия, DIY или Сделай сам] Ортолинейная сплит клавиатура — это что такое? Обзор Iris Keyboard
- [Системное программирование, Программирование микроконтроллеров, Компьютерное железо] Неожиданные детали работы Windows Firewall, настроенного по умолчанию. И эксперименты по перенастройке
- [DIY или Сделай сам, Здоровье, Электроника для начинающих] Как оценить пульсацию светодиодных ламп
- [Программирование микроконтроллеров, DIY или Сделай сам, Электроника для начинающих, Инженерные системы] Распределенный LED Контроллер управления светом (12V 6A)
- [C++, Программирование микроконтроллеров, DIY или Сделай сам, Медгаджеты] Одежда умная, но мы умнее: как мы делали футболку с контролем осанки
- [Программирование микроконтроллеров, Производство и разработка электроники, DIY или Сделай сам, Электроника для начинающих] Уличные часы изнутри, а если посложнее?
- [DIY или Сделай сам, Удалённая работа] Как смотреть людям в глаза во время видеоконференций (перевод)
- [Разработка под Linux, Разработка на Raspberry Pi, Компьютерное железо, Интернет вещей, DIY или Сделай сам] Встраиваемый компьютер AntexGate + 3G-модем. Полезные настройки для более стабильного интернет-соединения
Теги для поиска: #_programmirovanie_mikrokontrollerov (Программирование микроконтроллеров), #_programmirovanie_mikrokontrollerov (программирование микроконтроллеров), #_stm32, #_diy, #_programmirovanie_mikrokontrollerov (
Программирование микроконтроллеров
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:43
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В данной статье будет описан процесс подключение oled дисплея с контроллером ssd1306 разрешением 128x64 к микроконтроллеру stm32f103C8T6 по интерфейсу SPI. Также мне хотелось добиться максимальной скорости обновления дисплея, поэтому целесообразно использовать DMA, а программирование микроконтроллера производить с помощью библиотеки CMSIS. Подключение Подключать дисплей к микроконтроллеру будем по интерфейсу SPI1 по следующей схеме:
Передача данных происходит по возрастающему фронту сигнала синхронизации по 1 байту за кадр. Линии SCK и SDA служат для передачи данных по интерфейсу SPI, RES — перезагружает контроллер дисплея при низком логическом уровне, CS отвечает за выбор устройства на шине SPI при низком логическом уровне, DS определяет тип данных (команда — 1/данные — 0) которые передаются дисплею. Так как с дисплея ничего считать нельзя, вывод MISO использовать не будем. Организация памяти контроллера дисплея Перед тем, как выводить что-либо на экран, необходимо разобраться как в контроллере ssd1306 организована память. Вся графическая память (GDDRAM) представляет собой область 128*64=8192 бит=1 Кбайт. Область разбита на 8 страниц, которые представлены в виде в виде совокупности из 128-ми 8-ми битных сегментов. Адресация памяти происходит по номеру страницы и номеру сегмента соответственно. При таком методе адресации есть очень неприятная особенность — невозможность записать в память 1 бит информации, так как запись происходит по сегменту (по 8 бит). А так как для корректного отображения единичного пикселя на экране, необходимо знать состояние остальных пикселей в сегменте, целесообразно создать в памяти микроконтроллера буфер размером 1 Кбайт и циклически загружать его в память дисплея (тут и пригодится DMA), соответственно, производя его полное обновление. При использовании такого метода возможно пересчитать положение каждого бита в памяти на классические координаты x,y. Тогда для вывода на экран точки с координатами x и y воспользуемся следующим способом: displayBuff[x+(y/8)*SSD1306_WIDTH]|=(1<<(y%8));
А для того, чтобы стереть точку displayBuff[x+(y/8)*SSD1306_WIDTH]&=~(1<<(y%8));
Настройка SPI Как говорилось выше, подключать дисплей будем к SPI1 микроконтроллера STM32F103C8. Для удобства написания кода объявим некоторые константы и создадим функцию для инициализации SPI. #define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64 #define BUFFER_SIZE 1024 //Макросы для активации устройства на шине, сброса экрана и выбора команды/данных #define CS_SET GPIOA->BSRR|=GPIO_BSRR_BS2 #define CS_RES GPIOA->BSRR|=GPIO_BSRR_BR2 #define RESET_SET GPIOA->BSRR|=GPIO_BSRR_BS1 #define RESET_RES GPIOA->BSRR|=GPIO_BSRR_BR1 #define DATA GPIOA->BSRR|=GPIO_BSRR_BS3 #define COMMAND GPIOA->BSRR|=GPIO_BSRR_BR3 void spi1Init() { return; } Включим тактирование и произведем настройку выходов GPIO, как показано в таблице выше. RCC->APB2ENR|=RCC_APB2ENR_SPI1EN | RCC_APB2ENR_IOPAEN;//Включить тактирование SPI1 и GPIOA
RCC->AHBENR|=RCC_AHBENR_DMA1EN;//Включить тактирование DMA GPIOA->CRL|= GPIO_CRL_MODE5 | GPIO_CRL_MODE7;//PA4,PA5,PA7 в режим выходов 50MHz GPIOA->CRL&= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF7); GPIOA->CRL|= GPIO_CRL_CNF5_1 | GPIO_CRL_CNF7_1;//PA5,PA7 - выход с альтернативной функцией push-pull, PA4 - выход push-pull Далее произведем настройку SPI в режим master и частотой 18 Мгц. SPI1->CR1|=SPI_CR1_MSTR;//Режим ведущего
SPI1->CR1|= (0x00 & SPI_CR1_BR);//Делитель частоты на 2 SPI1->CR1|=SPI_CR1_SSM;//Программный NSS SPI1->CR1|=SPI_CR1_SSI;//NSS - high SPI1->CR2|=SPI_CR2_TXDMAEN;//Разрешить запросы DMA SPI1->CR1|=SPI_CR1_SPE;//включить SPI1 Настроим DMA. DMA1_Channel3->CCR|=DMA_CCR1_PSIZE_0;//Размер периферии 1байт
DMA1_Channel3->CCR|=DMA_CCR1_DIR;//Режим DMA из памяти в периферию DMA1_Channel3->CCR|=DMA_CCR1_MINC;//Включить инкремент памяти DMA1_Channel3->CCR|=DMA_CCR1_PL;//Высокий приоритет DMA Далее напишем функцию отправки данных по SPI (пока без DMA). Процесс обмена данными заключается в следующем:
void spiTransmit(uint8_t data)
{ CS_RES; SPI1->DR = data; while((SPI1->SR & SPI_SR_BSY)) {}; CS_SET; } Также напишем функцию непосредственно отправки команды экрану (Переключение линии DC производим только при передаче команды, а затем возвращаем ее в состояние «данные», так как команды передавать будем не так часто и в производительности не потеряем). void ssd1306SendCommand(uint8_t command)
{ COMMAND; spiTransmit(command); DATA; } Далее займемся функциями для работы непосредственно с DMA, для этого объявим буфер в памяти микроконтроллера и создадим функции для начала и остановки циклической отправки этого буфера в память экрана. static uint8_t displayBuff[BUFFER_SIZE];//Буфер экрана
void ssd1306RunDisplayUPD() { DATA; DMA1_Channel3->CCR&=~(DMA_CCR1_EN);//Выключить DMA DMA1_Channel3->CPAR=(uint32_t)(&SPI1->DR);//Занесем в DMA адрес регистра данных SPI1 DMA1_Channel3->CMAR=(uint32_t)&displayBuff;//Адрес данных DMA1_Channel3->CNDTR=sizeof(displayBuff);//Размер данных DMA1->IFCR&=~(DMA_IFCR_CGIF3); CS_RES;//Выбор устройства на шине DMA1_Channel3->CCR|=DMA_CCR1_CIRC;//Циклический режим DMA DMA1_Channel3->CCR|=DMA_CCR1_EN;//Включить DMA } void ssd1306StopDispayUPD() { CS_SET;//Дезактивация устройства на шине DMA1_Channel3->CCR&=~(DMA_CCR1_EN);//Выключить DMA DMA1_Channel3->CCR&=~DMA_CCR1_CIRC;//Выключить циклический режим } Инициализация экрана и вывод данных Теперь создадим функцию для инициализации самого экрана. void ssd1306Init()
{ } Для начала настроим CS, RESET и линию DC, а также произведем сброс контроллера дисплея. uint16_t i;
GPIOA->CRL|= GPIO_CRL_MODE2 |GPIO_CRL_MODE1 | GPIO_CRL_MODE3; GPIOA->CRL&= ~(GPIO_CRL_CNF1 | GPIO_CRL_CNF2 | GPIO_CRL_CNF3);//PA1,PA2,PA3 в режим выхода //Сброс экрана и очистка буфера RESET_RES; for(i=0;i<BUFFER_SIZE;i++) { displayBuff[i]=0; } RESET_SET; CS_SET;//Выбор устройства на шине Далее отправим последовательность команд для инициализации (Более подробно о них можно узнать в документации на контроллер ssd1306). ssd1306SendCommand(0xAE); //display off
ssd1306SendCommand(0xD5); //Set Memory Addressing Mode ssd1306SendCommand(0x80); //00,Horizontal Addressing Mode;01,Vertical ssd1306SendCommand(0xA8); //Set Page Start Address for Page Addressing ssd1306SendCommand(0x3F); //Set COM Output Scan Direction ssd1306SendCommand(0xD3); //set low column address ssd1306SendCommand(0x00); //set high column address ssd1306SendCommand(0x40); //set start line address ssd1306SendCommand(0x8D); //set contrast control register ssd1306SendCommand(0x14); ssd1306SendCommand(0x20); //set segment re-map 0 to 127 ssd1306SendCommand(0x00); //set normal display ssd1306SendCommand(0xA1); //set multiplex ratio(1 to 64) ssd1306SendCommand(0xC8); // ssd1306SendCommand(0xDA); //0xa4,Output follows RAM ssd1306SendCommand(0x12); //set display offset ssd1306SendCommand(0x81); //not offset ssd1306SendCommand(0x8F); //set display clock divide ratio/oscillator frequency ssd1306SendCommand(0xD9); //set divide ratio ssd1306SendCommand(0xF1); //set pre-charge period ssd1306SendCommand(0xDB); ssd1306SendCommand(0x40); //set com pins hardware configuration ssd1306SendCommand(0xA4); ssd1306SendCommand(0xA6); //set vcomh ssd1306SendCommand(0xAF); //0x20,0.77xVcc Создадим функции для заполнения всего экрана выбранным цветом и отображения одного пикселя. typedef enum COLOR
{ BLACK, WHITE }COLOR; void ssd1306DrawPixel(uint16_t x, uint16_t y,COLOR color){ if(x<SSD1306_WIDTH && y <SSD1306_HEIGHT && x>=0 && y>=0) { if(color==WHITE) { displayBuff[x+(y/8)*SSD1306_WIDTH]|=(1<<(y%8)); } else if(color==BLACK) { displayBuff[x+(y/8)*SSD1306_WIDTH]&=~(1<<(y%8)); } } } void ssd1306FillDisplay(COLOR color) { uint16_t i; for(i=0;i<SSD1306_HEIGHT*SSD1306_WIDTH;i++) { if(color==WHITE) displayBuff[i]=0xFF; else if(color==BLACK) displayBuff[i]=0; } } Далее в теле основной программы инициализируем SPI и дисплей. RccClockInit();
spi1Init(); ssd1306Init(); Функция RccClockInit() предназначена для настройки тактирования микроконтроллера. RccClockInit кодSPLint RccClockInit()
{ //Enable HSE //Setting PLL //Enable PLL //Setting count wait cycles of FLASH //Setting AHB1,AHB2 prescaler //Switch to PLL uint16_t timeDelay; RCC->CR|=RCC_CR_HSEON;//Enable HSE for(timeDelay=0;;timeDelay++) { if(RCC->CR&RCC_CR_HSERDY) break; if(timeDelay>0x1000) { RCC->CR&=~RCC_CR_HSEON; return 1; } } RCC->CFGR|=RCC_CFGR_PLLMULL9;//PLL x9 RCC->CFGR|=RCC_CFGR_PLLSRC_HSE;//PLL sourse:HSE RCC->CR|=RCC_CR_PLLON;//Enable PLL for(timeDelay=0;;timeDelay++) { if(RCC->CR&RCC_CR_PLLRDY) break; if(timeDelay>0x1000) { RCC->CR&=~RCC_CR_HSEON; RCC->CR&=~RCC_CR_PLLON; return 2; } } FLASH->ACR|=FLASH_ACR_LATENCY_2; RCC->CFGR|=RCC_CFGR_PPRE1_DIV2;//APB1 prescaler=2 RCC->CFGR|=RCC_CFGR_SW_PLL;//Switch to PLL while((RCC->CFGR&RCC_CFGR_SWS)!=(0x02<<2)){} RCC->CR&=~RCC_CR_HSION;//Disable HSI return 0; } Зальем весь дисплей белым цветом и посмотрим результат. ssd1306RunDisplayUPD();
ssd1306FillDisplay(WHITE); Нарисуем на экране в сетку шагом в 10 пикселей. for(i=0;i<SSD1306_WIDTH;i++)
{ for(j=0;j<SSD1306_HEIGHT;j++) { if(j%10==0 || i%10==0) ssd1306DrawPixel(i,j,WHITE); } } Функции работают корректно, буфер непрерывно записывается в память контроллера дисплея, что позволяет при отображении графических примитивов пользоваться декартовой системой координат. Частота обновления дисплея Так как буфер отправляется в память дисплея циклически, для приблизительного определения частоты обновления дисплея достаточно будет узнать время, за которое DMA осуществляет полную передачу данных. Для отладки в реальном времени воспользуемся библиотекой EventRecorder из Keil. Для того, чтобы узнать момент окончания передачи данных, настроим прерывание DMA на окончание передачи. DMA1_Channel3->CCR|=DMA_CCR1_TCIE;//Прерывание по завершении передачи
DMA1->IFCR&=~DMA_IFCR_CTCIF3;//Сбрасываем флаг прерывания NVIC_EnableIRQ(DMA1_Channel3_IRQn);//Включить прерывание Промежуток времени будем отслеживать с помощью функций EventStart и EventStop. Получаем 0.00400881-0.00377114=0.00012767 сек, что соответствует частоте обновления 4.2 Кгц. На самом деле частота не такая большая, что связано с неточностью способа измерения, но явно больше стандартных 60 Гц. Ссылки =========== Источник: habr.com =========== Похожие новости:
Программирование микроконтроллеров ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:43
Часовой пояс: UTC + 5