STM32 та LCD, швидка заливка екрану

В даний час набули поширення різні рідкокристалічні дисплеї, які відмінно підключаються до контролерів сімейства STM32. У даній статті мова піде про один з поширених контролерів STM32F103C8T6 і дисплеї 7" на контролері SSD1963. Обидва у вигляді закінчених вузлів легко доступні на Aliexpress і відносно недорого коштують. Звичайно, все розглянуте нижче справедливо і для інших дисплеїв з паралельним інтерфейсом і більшості контролерів STM32.

Ось так виглядають сполучаються:

image

image

У комплекті з дисплеєм була терморегулятори висновків та код ініціалізації для 51-го контролера з коментарями мовою виробника.

Коротко про підключення
Підключення дисплея полягає в подачі живлення 3.3 і 5 вольт на потрібні висновки і з'єднанні інформаційних ліній з контролером. Керуючі сигнали D/C, WE, RST підключаться до вільних ліній вводу-виводу на процесорі. У нашому випадку це D/C — PA1, WE — PA8, RST — PA2. Сигнали RD і CS можна не використовувати, при цьому на RD треба подати логічну одиницю, тобто підключити через резистор (в даному випадку 4,7 КОм), а на CS — «0», тобто підключити на землю.

ПриміткаДисплей налаштований виробником для роботи інтерфейсу в режимі 8080, і, згідно документації, сигнал CS «вибірка кристала» повинен бути задіяний:

image

Спочатку він так і працював. Однак, як показала перевірка, якщо ви не хочете використовувати шину даних для інших цілей, він не потрібен.

Далі потрібно підключити шину даних. У даного дисплея вона передбачається 16-розрядна, але при ініціалізації можна вибрати 8-й і 9-й бітні режими роботи. Тобто потрібно підключити як мінімум лінії дисплея DB0-DB7, як максимум ще й DB8-DB15. Для зручності програмування та мінімізації команд перетворення даних краще їх завести на одну групу вводу-виводу. Якщо розглядати варіант 16-розрядної шини даних, то вибирати на даному мікроконтролері не доводиться — тільки PB0 — PB15.

З'єднуємо їх відповідно з DB0-DB15 дисплея:

ПриміткаЗвичайно є ще й PA0-PA15, але якщо ми хочемо використовувати ST-Link для налагодження, то пара з них вже зайнята.

На гребінці дисплея залишається багато непідключених контактів, нехай це вас не бентежить. На ньому присутній слот SD карт пам'яті, сенсор екрану, навіть є розводка під мікросхему EEPROM пам'яті, але сама вона відсутня. Ці пристрої і займають решту роз'єму. До речі, під 40-контактний роз'єм дисплея ідеально підходить шлейф PATA жорстких дисків комп'ютера.

Ось так

Ініціалізація дисплея
Оригінальний код майже без змін перенесено в проект, додано тільки умовна компіляція для вибору розрядності шини даних (ініціалізація і команди йдуть по 8-бітній шині, незалежно від цього режиму).

Код
#define SET_LCD_RDS LCD_RDS_PORT->BSRR = LCD_RDS
#define RESET_LCD_RDS LCD_RDS_PORT->BRR = LCD_RDS
#define SET_LCD_WR LCD_WR_PORT->BSRR = LCD_WR
#define RESET_LCD_WR LCD_WR_PORT->BRR = LCD_WR
#define SET_LCD_RST LCD_RST_PORT->BSRR = LCD_RST
#define RESET_LCD_RST LCD_RST_PORT->BRR = LCD_RST

void SSD1963_Init (void)
{
uint16_t HDP=799;
uint16_t HT=928;
uint16_t HPS=46;
uint16_t LPS=15;
uint8_t HPW=48;

uint16_t VDP=479;
uint16_t VT=525;
uint16_t VPS=16;
uint16_t FPS=8;
uint8_t VPW=16;

RESET_LCD_RST;
delay_ms(5);
SET_LCD_RST;
delay_ms(5);

SSD1963_WriteCommand(0x00E2); //PLL multiplier, set PLL clock to 120M
SSD1963_WriteData(0x0023); //N=0x36 for 6.5 M, 0x23 for crystal 10M
SSD1963_WriteData(0x0002);
SSD1963_WriteData(0x0004);
SSD1963_WriteCommand(0x00E0); // PLL enable
SSD1963_WriteData(0x0001);
delay_ms(1);
SSD1963_WriteCommand(0x00E0);
SSD1963_WriteData(0x0003);
delay_ms(5);
SSD1963_WriteCommand(0x0001); // software reset
delay_ms(5);
SSD1963_WriteCommand(0x00E6); //PLL setting for PCLK, depends on resolution
SSD1963_WriteData(0x0003);
SSD1963_WriteData(0x00ff);
SSD1963_WriteData(0x00ff);

SSD1963_WriteCommand(0x00B0); //LCD SPECIFICATION
SSD1963_WriteData(0x0000);
SSD1963_WriteData(0x0000);
SSD1963_WriteData((HDP>>8)&0X00FF); //Set HDP
SSD1963_WriteData(HDP&0X00FF);
SSD1963_WriteData((VDP>>8)&0X00FF); //Set VDP
SSD1963_WriteData(VDP&0X00FF);
SSD1963_WriteData(0x0000);

SSD1963_WriteCommand(0x00B4); //HSYNC
SSD1963_WriteData((HT>>8)&0X00FF); //Set HT
SSD1963_WriteData(HT&0X00FF);
SSD1963_WriteData((HPS>>8)&0X00FF); //Set HPS
SSD1963_WriteData(HPS&0X00FF);
SSD1963_WriteData(HPW); //Set HPW
SSD1963_WriteData((LPS>>8)&0X00FF); //Set HPS
SSD1963_WriteData(LPS&0X00FF);
SSD1963_WriteData(0x0000);

SSD1963_WriteCommand(0x00B6); //VSYNC
SSD1963_WriteData((VT>>8)&0X00FF); //Set VT
SSD1963_WriteData(VT&0X00FF);
SSD1963_WriteData((VPS>>8)&0X00FF); //Set VPS
SSD1963_WriteData(VPS&0X00FF);
SSD1963_WriteData(VPW); //Set VPW
SSD1963_WriteData((FPS>>8)&0X00FF); //Set FPS
SSD1963_WriteData(FPS&0X00FF);

SSD1963_WriteCommand(0x00BA);
SSD1963_WriteData(0x0005); //GPIO[3:0] out 1

SSD1963_WriteCommand(0x00B8);
SSD1963_WriteData(0x0007); //GPIO3=input, GPIO[2:0]=output
SSD1963_WriteData(0x0001); //GPIO0 normal

SSD1963_WriteCommand(0x0036); //rotation
SSD1963_WriteData(0x0000);

SSD1963_WriteCommand(0x00F0); //pixel data interface
#if DATAPIXELWIDTH==16
SSD1963_WriteData(0x0003); //16 bit (565)
#endif
#if DATAPIXELWIDTH==9
SSD1963_WriteData(0x0006); // 9 bit
#endif
#if DATAPIXELWIDTH==8
SSD1963_WriteData(0x0000); // 8 bit
#endif

delay_ms(5);

SSD1963_WriteCommand(0x0029); //display on

SSD1963_WriteCommand(0x00d0);
SSD1963_WriteData(0x000d);
}

void SSD1963_WriteCommand(uint16_t commandToWrite)
{
LCD_DATA_PORT->ODR = commandToWrite;
RESET_LCD_RDS;
RESET_LCD_WR;
SET_LCD_WR;
}

void SSD1963_WriteData(uint16_t dataToWrite)
{
LCD_DATA_PORT->ODR = dataToWrite;
SET_LCD_RDS;
RESET_LCD_WR;
SET_LCD_WR;
}


У коді немає ініціалізації портів вводу-виводу і системного таймера, на основі якого реалізуються мілісекунд затримки (delay_ms()).

Після виконання ініціалізації:

tick_init(); // ініціалізація системного таймера
lcd_port_init(); // ініціалізація портів вводу-виводу
SSD1963_Init(); // ініціалізація дисплея

Ми бачимо «сміття» відеопам'яті на дисплеї:

Сміття

Заливка дисплея
Тепер хочеться стерти це сміття і залити екран яким-небудь кольором. В исходнике від виробника необхідний матеріал для написання коду присутній. Скористаємося ним.

Код
// Fills whole screen specified color
void SSD1963_SetArea(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2)
{
SSD1963_WriteCommand(0x002a);
SSD1963_WriteData((x1 >> 8) & 0xff);
SSD1963_WriteData(x1 & 0xff);
SSD1963_WriteData((x2 >> 8) & 0xff);
SSD1963_WriteData(x2 & 0xff);

SSD1963_WriteCommand(0x002a);
SSD1963_WriteData((y1 >> 8) & 0xff);
SSD1963_WriteData(y1 & 0xff);
SSD1963_WriteData((y2 >> 8) & 0xff);
SSD1963_WriteData(y2 & 0xff);
}

#if DATAPIXELWIDTH==16
void SSD1963_WriteDataPix(uint16_t pixdata)
{
LCD_DATA_PORT->ODR = pixdata;
SET_LCD_RDS;
RESET_LCD_WR;
SET_LCD_WR;
}
#endif

#if DATAPIXELWIDTH==9
void SSD1963_WriteDataPix(uint16_t pixdata)
{
LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xfe00) | ((pixdata >> 8) & 0x000f) | ((pixdata >> 7) & 0x01f0);
SET_LCD_RDS;
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xfe00) | ((pixdata << 1) & 0x01f7) | (pixdata & 0x0001);
RESET_LCD_WR;
SET_LCD_WR;
}
#endif

#if DATAPIXELWIDTH==8
void SSD1963_WriteDataPix(uint16_t pixdata)
{
LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xff00) | ((pixdata >> 8) & 0x00f8) | ((pixdata >> 9) & 0x0004);
SET_LCD_RDS;
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xff00) | ((pixdata >> 3) & 0x00fc);
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xff00) | ((pixdata << 3) & 0x00f8) | ((pixdata << 2) & 0x0004);
RESET_LCD_WR;
SET_LCD_WR;
}
#endif

void SSD1963_ClearScreen(uint16_t color)
{
unsigned int x,y;
SSD1963_SetArea(0, TFT_WIDTH-1 , 0, TFT_HEIGHT-1);
SSD1963_WriteCommand(0x002c);
for(x=0;x<TFT_WIDTH;x++){
for(y= 0;y<TFT_HEIGHT;y++){
SSD1963_WriteDataPix(color);
}
}
}


Як видно, код залежить від обраної розрядності шини. Відповідно залежить і час, необхідний для виконання передачі пікселя на дисплей. Для 16-розрядної шини піксель передається за один цикл передачі по шині даних, для 9-розрядної — за два, для 8-розрядної — за 3. Звідки ці дані? З документації на SSD1963.



В таблиці можна знайти розташування кожної складової кольору пікселя в залежності від режиму. У проекті використовуються режими 8 біт, 9 біт і 16 біт (565 формат). Як бачите, можна було також задіяти «чистий» формат 16 біт для більш точного кодування кольору, але він також вимагає трьох циклів передачі даних по шині. Формати 18 та 24 біт ми задіяти не можемо через наявність тільки 16-бітної шини на виході дисплея.

Отже, з якою швидкістю ми зможемо заповнити дисплей на процесорі з тактовою частотою 72 МГц?

176 мс — 16-розрядна шина
374 мс — 9-розрядна шина
470 мс — 8-розрядна шина

Не дуже швидко, звичайно, але для відображення повільно змінної інформації може бути достатньо. Більш привабливо виглядає звичайно 16-розрядна шина, і можливо комусь вона і підійде, але вона займає надто багато портів вводу-виводу, яких може не вистачити для підключення інших пристроїв до процесора.

Спробуємо розглянути компромісний варіант — 9 біт, як виграє майже 0.1 у 8-бітного варіанту за рахунок лише одного додаткового порту вводу-виводу.

Осцилограма

Оптимізація швидкості
Спробуємо прискорити процес заливки дисплея. Що якщо скоротити кількість логічних операцій всередині циклу?

// на вході 18-бітний колір RGB666
void SSD1963_WriteDataPix_9(uint32_t pixdata)
{
uint32_t tmp = (LCD_DATA_PORT->ODR & 0xfe00);
SET_LCD_RDS;
LCD_DATA_PORT->ODR = tmp | ((pixdata >> 9) & 0x01ff);
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = tmp | (pixdata & 0x01ff);
RESET_LCD_WR;
SET_LCD_WR;
}

// на вході 18-бітний колір RGB666
void SSD1963_ClearScreen_9(uint32_t color)
{
unsigned int x,y;
SSD1963_SetArea(0, TFT_WIDTH-1 , 0, TFT_HEIGHT-1);
SSD1963_WriteCommand(0x002c);
for(x=0;x<TFT_WIDTH;x++) {
for(y= 0;y<TFT_HEIGHT;y++) {
SSD1963_WriteDataPix_9(color);
}
}
}

Змінили кодування кольору замість 16-бітної змінної у форматі RGB565 використовуємо 32-бітну, задіявши тільки 18 з них у форматі RGB666. Крім того ввели тимчасову змінну для зберігання значення регістра LCD_DATA_PORT->ODR під час двох циклів виведення 9-бітних даних на шину. Тут треба зробити застереження, що це не завжди можливо, оскільки за час виведення стан інших портів групи GPIO B, налаштованих на висновок, може бути змінено в цей час в перериванні і програма буде працювати неправильно. Однак у нашому випадку таких проблем немає і ми перевіряємо чого ми досягли. Отже після першої оптимізації екран заповнюється в 9-бітному режимі за 298 мс. Якщо змінну не використовувати, і працювати з поточним станом порту, то приріст швидкості теж є, хоча і не такий значний — 335 мс:

void SSD1963_WriteDataPix_9(uint32_t pixdata)
{
SET_LCD_RDS;
LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xfe00) | ((pixdata >> 9) & 0x01ff);
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & 0xfe00) | (pixdata & 0x01ff);
RESET_LCD_WR;
SET_LCD_WR;
}

Можна також заради швидкості пожертвувати можливістю використання залишилися портів групи B в режимі виводу і прибрати логічні операції, що стосуються збереження їх стану:

void SSD1963_WriteDataPix_9(uint32_t pixdata)
{
SET_LCD_RDS;
LCD_DATA_PORT->ODR = pixdata >> 9;
RESET_LCD_WR;
SET_LCD_WR;

LCD_DATA_PORT->ODR = pixdata;
RESET_LCD_WR;
SET_LCD_WR;
}

Зрозуміло, що в режимі вводу і в альтернативних функції можливість використання збережуться, вони не залежать від регістру ODR.
Це дасть ще деяке прискорення, до 246 мс.

Осцилограма

Рухаємося далі.

Наступним етапом винесемо основний цикл перебору пікселів функцію на рівень глибше і спробуємо зробити програмний варіант емуляції роботи каналу DMA, прямого доступу до пам'яті. Для цього нам треба перенести лінію управління WE дисплея в групу, де розташована шина даних, тобто GPIO B. Нехай це буде PB9.

void SSD1963_WriteDataPix_9(uint32_t pixdata, uint32_t n){
static uint32_t dp[4];
uint8_t i;
SET_LCD_RDS;
RESET_LCD_WR;
dp[0] = (pixdata >> 9) & 0x01ff;
dp[1] = ((pixdata >> 9) & 0x01ff) | 0x0200;
dp[2] = pixdata & 0x01ff;
dp[3] = (pixdata & 0x01ff) | 0x0200;
for (;n;n--){
for (i=0;i < 4;i++) {
LCD_DATA_PORT->ODR = dp[i];
}
}

void SSD1963_ClearScreen_9(uint32_t color)
{
SSD1963_SetArea(0, TFT_WIDTH-1 , 0, TFT_HEIGHT-1);
SSD1963_WriteCommand(0x002c);
SSD1963_WriteDataPix_9(color, TFT_HEIGHT*TFT_WIDTH);
}

Як видно з коду, ми послідовно записуємо 9 варіанти даних до групи портів B, де крім 9-бітної шини даних розташований також сигнал WE. Операція " | 0x0200" — це як раз виставлення цього сигналу. Такий код дає чудовий приріст до 85 мс, а якщо замінити визначення масиву «static uint32_t dp[4]» на «static uint16_t dp[4], і до 75 мс. Для перевірки був замерен варіант з включенням режиму DMA і такий же передачею вмісту 4-х осередків в порт вводу-виводу. Результат всього лише 230 мс. Чому DMA повільніше? Все просто, в програмному режимі компілятор оптимізує код і всі 4 значення розміщуються в регістрах процесора, а не в пам'яті, а вибірка з пам'яті, яка виконується контролером DMA, йде значно повільніше, ніж робота з регістрами.
Скомпільований основний цикл виглядає так:

08000265: ldr r3, [pc, #24] ; (0x8000280 <SSD1963_WriteDataPix_9+84>)
 
08000267: str r6, [r3, #12]
 
08000269: str r5, [r3, #12]
 
0800026b: str r4, [r3, #12]
 
0800026d: str r1, [r3, #12]
 
0800026f: subs r2, #1
 
08000271: bne.n 0x8000266 <SSD1963_WriteDataPix_9+58>
 

У цьому варіанті, а також у варіанті з каналом DMA залишається обмеження на використання портів PB10-PB15. Проте на них можна вивести сигнали дисплея RST і D/C і врахувати їх у циклі, тоді обмежень будемо менше.

Таким чином, ми досягли максимальної швидкості заповнення цілком екрану або прямокутної області одним кольором. Начебто це межа, але можна ввести ще одне обмеження і рушити трохи далі.

Справа в тому, що в деяких застосуваннях дисплея не потрібен весь набір кольорів (в RGB656 — 65536 кольорів). Наприклад, в області АСУТП, де потрібно відображати стан виробничого об'єкта, або якесь текстове застосування, відображення повідомлень. Якщо це припущення вірно, і нам не потрібно відображати найкращу фото — і відеоматеріали, то спробуємо продовжити оптимізацію.
Розглянемо палітру, де у кожного кольору дорівнюють перша і друга частина даних, переданих по шині в дисплей. Тобто з 18 біт моделі RGB666, перші 9 біт рівні другим 9. Це дає нам 2^9=512 кольорів. Можливо комусь здасться недостатньо, але для побудови графіків або відображення алфавітно-цифрової інформації цілком може вистачити. Назвемо їх умовно «симетричні кольору».

Симетричні кольоруОсь вони виведені на екран:



Ось вибірка з них, 100 штук, більш наочно:



Що нам дає використання тільки цих квітів? Так то що для заповнення області нам не треба змінювати стан шини даних в процесі заливки. Досить перемикати стан сигналу WE і рахувати скільки разів ми це зробили. Більш того, можна інвертувати WE скільки завгодно довго, головне не менше ніж потрібно для заповнення області. Неважко порахувати, що раз на один піксел нам потрібно передати два блоки даних по шині, то потрібно 2 підтвердження сигналом WE. Відповідно на весь екран треба (Ширина_экрана*Длина_экрана*2) імпульсів, або 800*240*2=768000.

Як простіше генерувати імпульси. Звичайно! Можна використовувати таймер. TIM1 в даному контролері більш швидкий, ніж таймери TIM2-TIM4, т. к. знаходиться на більш швидкісній шині тактирования APB2. Дослідження показали, що включивши таймера в режимі ШІМ генератора з мінімальним дільником можна отримати час заповнення 32 мс! Зрозуміло що сигнал WE треба знімати з виходу таймера, наприклад PA8 (TIM1_CH1).

Можна ще збільшити швидкість заповнення? Виявилося так, просто подавши сигнал SYSCLK з виходу RCC_MCO на вхожий WE LCD. Це максимальна доступна частота на процесорі, 72 МГц. Час заповнення дисплея симетричним кольором становить 10.7 мс.
Відраховується час таймером, після чого по перериванню сигнал знімається, а порт перемикається в режим виводу.

Код
//ініціалізація таймера
void SSD1963_TimInit2(void){
TIM_TimeBaseInitTypeDef Timer;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseStructInit(&Timer);
Timer.TIM_Prescaler = 72-1;
Timer.TIM_Period = 10000;
Timer.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM4, &Timer);
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
NVIC_EnableIRQ(TIM4_IRQn);
}

void SSD1963_WriteDataPix(uint32_t pixdata, uint32_t n){
GPIO_InitTypeDef GPIO_InitStr;
SET_LCD_RDS;
LCD_DATA_PORT->ODR = (LCD_DATA_PORT->ODR & ~0x01ff) | (pixdata & 0x01ff);
GPIO_InitStr.GPIO_Pin = LCD_WR; 
GPIO_InitStr.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LCD_WR_PORT, &GPIO_InitStr); // включення альтернативної функції
TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); // заборона виклику переривання під час оновлення дільника
if (n > 32000 ){
TIM_PrescalerConfig(TIM4, 72 - 1, TIM_PSCReloadMode_Immediate); // період 1 мкс
TIM4->CNT = (uint16_t) (n / 36); // обчислюємо час на мкс на заливку
} else {
TIM_PrescalerConfig(TIM4, 0, TIM_PSCReloadMode_Immediate); // період 1/72 мкс (мінімальний)
TIM4->CNT = (uint16_t) (n * 2 - 1); // два такти на заливку пікселя
}
TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // скидання запиту переривання
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); // дозвіл переривання
RCC_MCOConfig(RCC_MCO_SYSCLK); //MCO виберемо джерело
TIM4->CR1 |= TIM_CR1_CEN; //запуск таймера
}

void TIM4_IRQHandler()
{
GPIO_InitTypeDef GPIO_InitStr;
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // скидання запиту переривання
TIM_Cmd(TIM4, DISABLE); // таймера вимкнення
RCC_MCOConfig(RCC_MCO_NoClock); // вимикання SYSCLK на виході MCO
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP; 
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStr.GPIO_Pin = LCD_WR;
GPIO_Init(LCD_WR_PORT, &GPIO_InitStr); // перемикання порту в режим виведення
}
}

void SSD1963_ClearScreen_9(uint32_t color)
{
SSD1963_SetArea(0, TFT_WIDTH-1 , 0, TFT_HEIGHT-1);
SSD1963_WriteCommand(0x2c);
SSD1963_WriteDataPix(color, TFT_HEIGHT*TFT_WIDTH);
}

int main(void){
tick_init(); // ініціалізація системного таймера
lcd_port_init(); // ініціалізація портів вводу-виводу
SSD1963_Init(); // ініціалізація дисплея
SSD1963_TimInit2(); // ініціалізація таймера TIM4
SSD1963_ClearScreen_9(0x1ff); // Заливка білим кольором екрану
while(1) {}
}



Таймер відраховує час з точністю 1/72 мкс для кількості точок, меншого 32000 і з точністю 1 мкс для більшої кількості точок. Це пов'язано з розрядністю лічильника таймера. Враховуючи, що потрібен якийсь час на обробку переривання при виключенні таймер, сигнал на виході MCO знімається трохи пізніше, ніж потрібно, з невеликим запасом. Експериментально встановлено, що це близько 10-11 тактів частоти процесора. Таким чином, можна сказати що є поріг використання даної методики, при якому вона залишається швидше, незважаючи на накладні витрати на ініціалізацію таймера і RCC_MCO і відключення. Квадрат 2х2 пікселя ймовірно вигідніше програмно заповнювати по циклу.

В якості висновку можна сказати що, додавши деякі обмеження, було зменшено час заповнення екрана з 375 до 11 мс. Крім того, заповнення йде без участі процесора, який в цей час може виконувати інші завдання.

Буду радий зауважень і доповнень.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.