Video на stm32duino: ov7670 + stm32f103c8t6 + ili9341
- Войдите на сайт для отправки комментариев
Ср, 22/08/2018 - 11:29
Скажу честно: зачем мне может понадобиться подобная конструкция, я не знаю. Сам на форуме несколько раз заявлял, что Ардуино и видео - вещи несовместимые. Но вот соблазнился на низкую цену и заказал себе камеру (вдруг когда-нибудь пригодится), а когда получил, обнаружил, что проверить работоспособность не так просто.
В общем, проект не является утилитарным, а подразумевает исключительно спортивный интерес.
Итак, что имеем:
- самую дешевую камеру (что-то в районе $2),
- самый дешевый дисплей 320х240 - увы, только в виде шилда для Uno (все другие варианты - дороже).
- AVR вряд ли подходят по производительности, на Due неудобно (и не полностью) разведены порты, Due Core - жаба душит, а BluePill - самый раз по всем позициям, особенно, учитывая, что это самый дешевый 32-разрядный контроллер.
Итак, получилась конструкция из трех самых дешевых компонент.
Кстати, 3 штучки BluePill в свое время заказал, но так и не удосужился их даже попробовать. Теперь, наконец, руки дошли. Один, кстати, оказался неисправным :(
По логике, можно было бы просто соединить выход камеры с входом дисплея, предварительно настроив их так, чтобы они понимали сигнал друг друга. А контроллер задействовать только в шине управления. Тогда и Pro Mini за глаза хватило бы. Но как же спортивный интерес? Опять же, если мы говорим об обработке, то процесс выглядит так: чтение_с_камеры->обработка_в_контроллере->вывод_на_дисплей. Так что, чтобы иметь возможность как-то включить обработку, нужно по меньшей мере иметь первую и последнюю стадии. Вот этим, собственно, и займемся.
Ко всему оказалось, что BluePill еще и оптимален по количеству потов ввода-вывода:
- для камеры нужно: 8 - шина данных, 2 - шина SCCB, 4-5 - шина управления,
- для дисплея нужно: 8 - шина данных, 4-5 - шина управления,
итого: 26-28 контактов.
У BluePill всего 35 контактов, некоторые из которых, правда, 8 используются по другому назначению:
- PB2 - используется в качестве BOOT1 - на гребенку не разведен,
- PA13-PA14 - используются ST-Link - на гребенку не разведены,
- PA11-PA12 - используются USB - на гребенке присутствуют,
- PC13 - используется для светодиода-индикатора - на гребенке присутствует,
- PC14-PC15 - соединены с кварцем на 32768 для RTC - на гребенке присутствуют.
т.е. получается, что всего в обрез.
По факту, решил отказаться от RESET на дисплее, благо для этого есть команда. Хотя, вероятно, лучше было бы отказаться от сигнала READ дисплея, т.к. он включен через преобразователь уровня, а подавать на 3-вольтовый контроллер 5 Вольт сразу по 8 каналам - чревато.
При проектировании распиновки выяснилась одна неприятная особенность: если мы хотим передавать данные с камеры на дисплей без дополнительных манипуляций, номера пинов шины данных порта А должны соответствовать номерами пинов шины данных порта В, причем, в порте В занят один пинов из младшей половины, а в порте А - несколько пинов из старшей. Поэтому шину данных пришлось расположить на пинах с 3 по 10.
Вот что получилось в результате:

Для отладки на схеме предусмотрел несколько точек подключения логического анализатора или осциллографа (показаны кружками), а также немного разделил выход данных SCCB контроллера и камеры - опять же для облегчения отладки.
Посмотрев на дохленький стабилизатор питания BluePill не рискнул нагружать его еще и камерой, поэтому предусмотрел внешний стабилизатор.
andriano. жду продолжения. DMA будет?
Буду пробовать несколько вариантов. Среди них, возможно, будет и DMA. С stm32 работаю меньше двух недель, но пощупать DMA желание есть. Пока есть только опыт программирования DMA на IBM PC под DOS.
Мне кажется DMA тут не светит, c обычными портами оно не умеет общаться.
С какого это перепуга??? Не факт что будет быстрее с ДМА... из-за латентности... но работать будет...
#ifndef SCCB_H #define SCCB_H #include <Arduino.h> #define SCCB_SDA 0x0001 #define SCCB_SCL 0x0002 class cSCCB { public: void pause(); uint8_t readRegister(uint8_t r); void writeRegister(uint8_t r, uint8_t d); private: void start(); void writeBit(bool b); void writeByte(uint8_t d); uint8_t readBit(); uint8_t readByte(); void stop(); }; #endif#include "sccb.h" void cSCCB::start() { delayMicroseconds(4); GPIOB_BASE->BRR = SCCB_SDA; delayMicroseconds(2); GPIOB_BASE->BRR = SCCB_SCL; delayMicroseconds(2); } void cSCCB::writeBit(bool b){ if(b) GPIOB_BASE->BSRR = SCCB_SDA; else GPIOB_BASE->BRR = SCCB_SDA; delayMicroseconds(2); GPIOB_BASE->BSRR = SCCB_SCL; delayMicroseconds(4); GPIOB_BASE->BRR = SCCB_SCL; delayMicroseconds(2); } void cSCCB::writeByte(byte d){ for(int i = 0; i < 8; i++) { writeBit(d & 0x80); d <<= 1; } writeBit(1); // ASK } byte cSCCB::readBit(){ delayMicroseconds(2); GPIOB_BASE->BSRR = SCCB_SCL; delayMicroseconds(2); byte b = (byte)((GPIOB_BASE->IDR & SCCB_SDA) != 0); delayMicroseconds(2); GPIOB_BASE->BRR = SCCB_SCL; delayMicroseconds(2); return b; } byte cSCCB::readByte(){ byte d = readBit(); for(int i = 1; i < 8; i++) { d = (d << 1) + readBit(); } writeBit(1); // ASK return d; } void cSCCB::stop(){ GPIOB_BASE->BRR = SCCB_SDA; delayMicroseconds(2); GPIOB_BASE->BSRR = SCCB_SCL; delayMicroseconds(6); GPIOB_BASE->BSRR = SCCB_SDA; } void cSCCB::pause(){ delayMicroseconds(8); } byte cSCCB::readRegister(byte r) { pause(); start(); writeByte(0x42); writeByte(r); stop(); pause(); start(); writeByte(0x43); byte d = readByte(); stop(); pause(); return d; } void cSCCB::writeRegister(byte r, byte d) { pause(); start(); writeByte(0x42); writeByte(r); writeByte(d); stop(); pause(); }С какого это перепуга??? Не факт что будет быстрее с ДМА... из-за латентности... но работать будет...
мб из за того что DMA не умеет работать с GPIO
мб если настроить
для камеры :
DMA_CHx_SrcAddr = &GPIOA (у GPIO регистра есть адресс)
DMA_CHy_DstAddr = buffer;
для экрана :
DMA_CHz_SrcAddr = buffer
DMA_CHw_DstAddr = &GPIOB
мб из за того что DMA не умеет работать с GPIO
Похоже, что действительно можно работать с GPIO, но не как с переферией, а как с адресным пространством. Хорошо бы кто нибудь разобрался и выложил примерчик для stmduino :-))
Похоже, что действительно можно работать с GPIO, но не как с переферией, а как с адресным пространством.
Да, на STM32 вся периферия смаплена в области памяти и к любому GPIO можно обращаться, просто читая или записывая данные по определенному адресу.
К сожалению, на практике пока не пробовал :(
мб из за того что DMA не умеет работать с GPIO
Это DMA не умеет работать с GPIO... или вы???
Умеет работать... и точка...
К сожалению, на практике пока не пробовал :(
Хорошая вещь... кстати...
Первым делом после пайки платы подключил к ней дисплей.
Вывод на дисплей - одна из важных частей проекта, нужно було его как-то пощупать и пооптимизировать.
Вот что получилось:
// для ILI9341 на STM32F102C8T6 72 MHz (оптимизация -O1) // скорость заполнения (1 пиксель на итерацию) : 5.52 Мпикс/с (FPS= 71.9) // скорость заполнения (8 пикселей на итерацию) : 8.32 Мпикс/с (FPS=108.3) // скорость заполнения (64 пикселя на итерацию) : 8.88 Мпикс/с (FPS=115.6) #define LCD_RD 0x8000 // PB15 - не используется (если использовать, нужно согласовать уровни сигналов) #define LCD_WR 0x4000 // PB14 #define LCD_RS 0x2000 // PB13 #define LCD_CS 0x1000 // PB12 #define LCD_RESET 0x0800 // PB11 #define DATA_MASK 0x07f8 // PB3-PB10 (8, 9, 2-7) #define CTRL_MASK_DEFAULT 0xf803 // бит PB2 не используется, поэтому не 0xf107: все управляющие биты HIGH #define CTRL_MASK_WR_RS_SC 0x8803 // маска для записи команд #define CTRL_MASK_WR_SC 0xa803 // маска для записи данных // pin PB15 PB14 PB13 PB12 PB11 PB10 PB9 PB8 PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 // func RD WR RS CS REST D7 D6 D5 D4 D3 D2 D1 D0 - SDA SCL // default 1 1 1 1 1 x x x x x x x x - 1 1 : ODR // mode 3 3 3 3 3 3 3 3 3 3 3 3 3 4 7 7 : CRH/CRL void Lcd_Write_Com(unsigned char d) // вывод команды: опустить PB13, вывести байт, поднять PB13 { GPIOB_BASE->ODR = CTRL_MASK_WR_RS_SC | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Data(unsigned char d) { GPIOB_BASE->ODR = CTRL_MASK_WR_SC | (d << 3); GPIOB_BASE->BSRR = LCD_WR; } void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2) // позиционирование на экране (оптимизировано) { GPIOB_BASE->ODR = 0x8953; // команда 0x2a - Column Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC; // считая, что x1 <= 240, старший байт равен 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC; // считая, что x2 <= 240, старший байт равен 0 GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((x2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x895b; // команда 0x2b - Page Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((y2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x8963; // команда 0x2c - Mempry Write GPIOB_BASE->BSRR = LCD_WR; } void Lcd_Init(void) // вызываем один раз, поэтому по скорости не оптимизируем { GPIOB_BASE->BSRR = LCD_RESET; delay(5); GPIOB_BASE->BRR = LCD_RESET; delay(15); GPIOB_BASE->BSRR = LCD_RESET; delay(15); Lcd_Write_Com(0xCB); Lcd_Write_Data(0x39); Lcd_Write_Data(0x2C); Lcd_Write_Data(0x00); Lcd_Write_Data(0x34); Lcd_Write_Data(0x02); Lcd_Write_Com(0xCF); Lcd_Write_Data(0x00); Lcd_Write_Data(0XC1); Lcd_Write_Data(0X30); Lcd_Write_Com(0xE8); Lcd_Write_Data(0x85); Lcd_Write_Data(0x00); Lcd_Write_Data(0x78); Lcd_Write_Com(0xEA); Lcd_Write_Data(0x00); Lcd_Write_Data(0x00); Lcd_Write_Com(0xED); Lcd_Write_Data(0x64); Lcd_Write_Data(0x03); Lcd_Write_Data(0X12); Lcd_Write_Data(0X81); Lcd_Write_Com(0xF7); Lcd_Write_Data(0x20); Lcd_Write_Com(0xC0); //Power control Lcd_Write_Data(0x23); //VRH[5:0] Lcd_Write_Com(0xC1); //Power control Lcd_Write_Data(0x10); //SAP[2:0];? BT[3:0] Lcd_Write_Com(0xC5); //VCOM control 1 Lcd_Write_Data(0x3e); // VMH[6] Contrast Lcd_Write_Data(0x28); // VML[6] Lcd_Write_Com(0xC7); //VCOM control 2 Lcd_Write_Data(0x86); // VMF[6] Lcd_Write_Com(0x36); // Memory Access Control Lcd_Write_Data(0x48); // MY=0, MX=1, MV=0, ML=0, BGR=1, MH=0, X=0, X=0 Lcd_Write_Com(0x3A); // Pixel Format Set Lcd_Write_Data(0x55); // X=0, DPI[3]=101, X=0, DBI[3]=101 Lcd_Write_Com(0xB1); // Frame Control (in Normal Mode) Lcd_Write_Data(0x00); // X, X, X, X, X, X, DIVA[2]=00 (Division Rate = 0) Lcd_Write_Data(0x18); // X, X, X, RTNA[5]=11000 (Frame Rate = 79Hz) Lcd_Write_Com(0xB6); // Display Function Control Lcd_Write_Data(0x08); // X, X, X, X, PTG[2]=10, PT[2]=00 Lcd_Write_Data(0x82); // REV=1, GS=0, SS=0, SM=0, ISC[4]=0010 Lcd_Write_Data(0x27); // X, X, PCDIV[6]=100111 Lcd_Write_Com(0x11); //Exit Sleep // Sleep OUT delay(120); Lcd_Write_Com(0x29); //Display ON Lcd_Write_Com(0x2c); // Memory Write } #define PUTPIXEL { \ GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c >> 5) & DATA_MASK); \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = CTRL_MASK_WR_SC | ((c << 3) & DATA_MASK); \ GPIOB_BASE->BSRR = LCD_WR; \ } #define PUT_8_PIXELS { \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ } void H_line(unsigned int x, unsigned int y, unsigned int l, unsigned int c) { Address_set(x, y, x+l-1, y); for(unsigned int i=0; i<l; i++) { PUTPIXEL } } void V_line(unsigned int x, unsigned int y, unsigned int l, unsigned int c) { Address_set(x, y, x, y+l-1); for(unsigned int i=0; i<l; i++) { PUTPIXEL } } void Rect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c) { H_line(x , y , w, c); H_line(x , y+h-1, w, c); V_line(x , y , h, c); V_line(x+w-1, y , h, c); } void Rectf(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c) { unsigned int i; for(i=0; i<w; i++) { V_line(x+i , y, h, c); } } void LCD_Clear_1px(unsigned int c) { Address_set(0,0,239,319); for(unsigned int i=0; i<(320*240); i++){ // для 2 пикселей за итерацию PUTPIXEL } } void LCD_Clear_8px(unsigned int c) { Address_set(0,0,239,319); for(unsigned int i=0; i<(40*240); i++){ // для 8 пикселей за итерацию PUT_8_PIXELS } } void LCD_Clear_64px(unsigned int c) { Address_set(0,0,239,319); for(unsigned int i=0; i<(5*240); i++){ // для 64 пикселей за итерацию PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS PUT_8_PIXELS } } void setup() { Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. } // default 1 1 1 1 1 x x x x x x x x - 1 1 // mode 3 3 3 3 3 3 3 3 3 3 3 3 3 4 7 7 GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // все управляющие HIGH GPIOB_BASE->CRL = 0x33333477; // пины PB7-PB3 - выход, PB2 - вход (не исп.), PB1-PB0 - выход с ОК (I2C) GPIOB_BASE->CRH = 0x33333333; // пины PB15-PB8 - выход GPIOB_BASE->ODR = CTRL_MASK_DEFAULT; // Lcd_Init(); LCD_Clear_1px(0xffff); } void loop() { long t0 = micros(); LCD_Clear_1px(0x07E0); // GREEN long t1 = micros(); delay(500); long t2 = micros(); LCD_Clear_1px(0x001F); // BLUE long t3 = micros(); delay(500); long t4 = micros(); LCD_Clear_1px(0xf800); // RED long t5 = micros(); delay(500); int avgTime; Serial.print("Fill Screen x1 pixel: "); avgTime = ((t1-t0) + (t3-t2) + (t5-t4) + 1) /3; Serial.print(avgTime); Serial.print(" us, FPS = "); Serial.print(1000000.0/avgTime); Serial.print(", ("); Serial.print(1.0/avgTime*320*240); Serial.println(") Mpix/s"); t0 = micros(); LCD_Clear_8px(0x07E0); // GREEN t1 = micros(); delay(500); t2 = micros(); LCD_Clear_8px(0x001F); // BLUE t3 = micros(); delay(500); t4 = micros(); LCD_Clear_8px(0xf800); // RED t5 = micros(); delay(500); Serial.print("Fill Screen x8 pixels: "); avgTime = ((t1-t0) + (t3-t2) + (t5-t4) + 1) /3; Serial.print(avgTime); Serial.print(" us, FPS = "); Serial.print(1000000.0/avgTime); Serial.print(", ("); Serial.print(1.0/avgTime*320*240); Serial.println(") Mpix/s"); t0 = micros(); LCD_Clear_64px(0x07E0); // GREEN t1 = micros(); delay(500); t2 = micros(); LCD_Clear_64px(0x001F); // BLUE t3 = micros(); delay(500); t4 = micros(); LCD_Clear_64px(0xf800); // RED t5 = micros(); delay(500); Serial.print("Fill Screen x64 pixels: "); avgTime = ((t1-t0) + (t3-t2) + (t5-t4) + 1) /3; Serial.print(avgTime); Serial.print(" us, FPS = "); Serial.print(1000000.0/avgTime); Serial.print(", ("); Serial.print(1.0/avgTime*320*240); Serial.println(") Mpix/s"); for(int i=0;i<100;i++) { unsigned int x = random(220); unsigned int y = random(300); unsigned int w = random(240 - x); unsigned int h = random(320 - y); unsigned int c = random(65535); Rect(x,y,w,h,c); // rectangle at x, y, with, hight, color } long t8 = micros(); Rect(0, 0, 240, 320, 0xffff); Rect(1, 1, 238, 318, 0x001f); Rect(2, 2, 236, 316, 0x001f); Rect(3, 3, 234, 314, 0xffff); delay(2000); }Как выяснилось, дисплей оптимизирован под групповые операции, т.е. можно определить прямоугольный регион, а дальше просто писать туда данные, и он будет последовательно (строка за строкой) заполняться.
Общие результаты таковы:
Проверьте -Ofast. Я вроде это уже советовал.
Спасибо, дойдут руки - проверю. Но для этого надо ковырять IDE, как там провесить команды, не выведенные в меню. Пока что есть более интересное занятие.
И, кстати, мне не попадалась таблица команд с растактовкой для stm32f103c. Ни у кого нет подходящей ссылочки?
Значит... либо ардуина шалит... либо с ключами ЖЦЦ не разобрались... На Кейле всё нормально... начиная с -O1 и выше...
И, кстати, мне не попадалась таблица команд с растактовкой для stm32f103c. Ни у кого нет подходящей ссылочки?
здесь
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337e/DDI0337E_cortex_m3_r1p1_trm.pdf
Глава 18
Спасибо.
Ну а с ключами gcc, а также вообще с тем, как до них добраться через Arduino IDE, я действительно разбираться даже не начинал.
Ну и о том, а не пора ли переходить на Кейл либо какую другую среду, тоже задумывался, но пока к единому мнению не пришел.
#include "sccb.h" #include "ili9341_a.h" #define PA0_In (*((volatile unsigned long *) 0x42210100 )) // HS #define PA1_In (*((volatile unsigned long *) 0x42210104 )) // PCLK #define PA15_In (*((volatile unsigned long *) 0x4221013C )) // VS #define PB12_Out (*((volatile unsigned long *) 0x422181B0 )) // LCD_CS #define PB13_Out (*((volatile unsigned long *) 0x422181B4 )) // LCD_RS #define PB14_Out (*((volatile unsigned long *) 0x422181B8 )) // LCD_WR #define PC13_Out (*((volatile unsigned long *) 0x422201B4 )) // LED cSCCB sccb; struct regval_list{ uint8_t reg_num; uint16_t value; }; const struct regval_list QVGA_A [] = { {0x12, 0x04}, // COM7 : RGB format D1 - color bar {0x40, 0xD0}, // COM15 : RGB 5:6:5 {0x0c, 0x04}, // COM3 : enable downsamp/crop/window COM3_DCWEN ([6]=MSB LSB swap)def=0 {0x3e, 0x19}, // COM14 : DCW_Scale | manually_scaling_parameter | PCLK_divide_by_2 {0x72, 0x11}, // SCALING DCWCTR : vertical_down_sample_by_2 | horizontal_down_sample_by_2 {0x73, 0x81}, // SCALING PCLK_DIV : def=f1 !!!! | clock divider by 2 !!! бит d7 включает камеру!!! {0x17, 0x16}, // HSTART : horizontal frame start 22*8 + ... (176+) def=11, было=16 {0x18, 0x04}, // HSTOP : horizontal frame end 4*8 + ... = (32+) def=61, было=04 {0x32,0x24}, // HREF : ?0xa4? [32] [17]+4, [18]+4 {0x19,0x02}, // VSTART : 2*4 + ... (8+) def=03 ,было=02 {0x1a,0x7a}, // VSTOP : 122*4 + ... (488+) def=7b, было=7a {0x03, 0x0a}, // VREF : [19]+2, [1a]+2 // {0x70, 0xca}, // SCALING XSC : добавляет к color bar серый клин // {0x71, 0xb5}, // SCALING YSC : for 8-bar color bar // {0x2b, 0x08}, // EXHCL : dummy pixel in horiz direction - добавляет в конец? // {0x30, 0x10}, // HSYST : rising H delay (default=8) {0xaa, 0x94}, // HAECC7 : histogram - instead 0x14 // {0xb1, 0x04}, // ABLC1 : auto black level (00) - не заметно // {0x6f, 0x90}, // AWBCTR0 : AutoWhiteBalance mode, (9a) - не заметно // {0x01, 0x80}, // BLUE : Blue Gain // {0x02, 0xc0}, // RED : Red Gain // {0x6a, 0x4f}, // GGAIN : Green gain {0xff, 0xff}, // END MARKER }; void wrSensorRegs8_8(const struct regval_list reglist[]){ uint8_t reg_addr, reg_val; const struct regval_list *next = reglist; while ((reg_addr != 0xff) | (reg_val != 0xff)){ reg_addr = next->reg_num; reg_val = next->value; sccb.writeRegister(reg_addr, reg_val); next++; } } void timer2ch3_init(){ // timer 2 channel 3 RCC_BASE->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; // enable port A and AFIO peripherals RCC_BASE->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM4 peripheral GPIOA_BASE->CRL = (GPIOA_BASE->CRL & ~(0x0F<<8)) | (0x0B<<8); // (*) // configure A2 to alternative output mode // set up timer TIMER2_BASE->CR1 = (1 << 7); // TIM_CR1_ARPE; // count up to ARR, no divisor, auto reload TIMER2_BASE->ARR = 1; // period TIMER2_BASE->PSC = 8; // prescaler TIMER2_BASE->EGR = 1; // TIM_EGR_UG; // generate an update event to reload the prescaler value immediately // set up compare (ch3) TIMER2_BASE->CCER &= ~(0x0300); // (*)~(TIM_CCER_CC3E | TIM_CCER_CC3P); // disable cc3 output, clear polarity TIMER2_BASE->CCR3 = 1; // (*) cc3 period TIMER2_BASE->CCMR2 &= 0xFF00; // (*) TIMER2_BASE->CCMR2 |= 0x0068; // (*) TIMER2_BASE->CCER |= 0x0100; // (*) TIM_CCER_CC3E; // enable cc4 output TIMER2_BASE->CR1 |= 1; //TIM_CR1_CEN; // run timer } void setup(){ GPIOC_BASE->CRH = 0x44344444; // PC13 olly - LED // default 1 1 1 1 1 x x x x x x x x - 1 1 // mode 3 3 3 3 3 3 3 3 3 3 3 3 3 4 7 7 GPIOB_BASE->ODR = INV_DATA_MASK; // 0xf103 GPIOB_BASE->CRL = 0x33333477; // пины PB7-PB3 - выход, PB2 - вход (не исп.), PB1-PB0 - выход с ОК GPIOB_BASE->CRH = 0x33333333; // пины PB15-PB8 - выход GPIOB_BASE->ODR = INV_DATA_MASK; // 0xf103 Lcd_Init(); LCD_Clear(0xffff); digitalWrite(PC13, HIGH); timer2ch3_init(); delay(300); sccb.writeRegister(0x12, 0x80); delay(200); wrSensorRegs8_8(QVGA_A); // QVGA_A delay(100); } void loop(){ // VSYNC while (!PA15_In);//wait for high VS while (PA15_In);//wait for low VS int y = 240; static uint16_t buff[640]; // буфер для строки while (y--){ int x = 640; Address_set(y,0,y,319); PB13_Out = 0; noInterrupts(); PB13_Out = 1; while (PA0_In);//wait for low HS while (!PA0_In);//wait for high HS while (PA1_In);//wait for low PCLK while (!PA1_In);//wait for high PCLK while (x--){ //PCLK while (!PA1_In);//wait for high PCLK buff[x] = GPIOA_BASE->IDR; // вариант с буфером строки // GPIOB_BASE->ODR = 0xa803 | (GPIOA_BASE->IDR & DATA_MASK); // вариант // GPIOB_BASE->BRR = LCD_WR; // без // GPIOB_BASE->BSRR = LCD_WR; // буфера строки while (PA1_In);//wait for low PCLK } PB13_Out = 0; interrupts(); PB13_Out = 1; x = 640; // while (x--){ // вариант GPIOB_BASE->ODR = 0xa803 | (buff[x] & DATA_MASK); // с GPIOB_BASE->BRR = LCD_WR; // буфером GPIOB_BASE->BSRR = LCD_WR; // строки } // } }И включаемый файл (правда, слепленный не по фен-шую)
#ifndef ILI9341_A_H #define ILI9341_A_H #include <Arduino.h> #define LCD_RD 0x8000 // PB15 A0 - не используется (если использовать, нужно согласовать уровни сигналов) #define LCD_WR 0x4000 // PB14 A1 #define LCD_RS 0x2000 // PB13 A2 #define LCD_CS 0x1000 // PB12 A3 #define LCD_RESET 0x0800 // PB11 A4 #define DATA_MASK 0x07f8 // PB3-PB10 (8, 9, 2-7) #define INV_DATA_MASK 0xf803 // бит PB2 не используется, поэтому не 0xf107 // pin PB15 PB14 PB13 PB12 PB11 PB10 PB9 PB8 PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 // func RD WR RS CS REST D7 D6 D5 D4 D3 D2 D1 D0 - SDA SCL // default 1 1 1 1 1 x x x x x x x x - 1 1 // mode 3 3 3 3 3 3 3 3 3 3 3 3 3 4 7 7 inline void myDelay(volatile unsigned long n) { while (n--); } inline void selectLCD() { GPIOB_BASE->BRR = LCD_CS; /*myDelay(10);*/ } inline void releaseLCD() { GPIOB_BASE->BSRR = LCD_CS; /*myDelay(10);*/ } inline void Lcd_Write_Com(unsigned char d) // вывод команды: опустить PB13, вывести байт, поднять PB13 { GPIOB_BASE->ODR = 0x8803 | (d << 3); //~LCD_WR; // 0xe803 GPIOB_BASE->BSRR = LCD_WR; } inline void Lcd_Write_Data(unsigned char d) { GPIOB_BASE->ODR = 0xa803 | (d << 3); //~LCD_WR; // 0xe803 GPIOB_BASE->BSRR = LCD_WR; } void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2) { /* Lcd_Write_Com(0x2a); // Column Address Set Lcd_Write_Data(x1>>8); // SC[15:8] Lcd_Write_Data(x1); // SC[7:0] Lcd_Write_Data(x2>>8); // EC[15:8] Lcd_Write_Data(x2); // EC[7:0] Lcd_Write_Com(0x2b); // Page Address Set Lcd_Write_Data(y1>>8); // SP[15:8] Lcd_Write_Data(y1); // SP[7:0] Lcd_Write_Data(y2>>8); // EP[15:8] Lcd_Write_Data(y2); // EP[7:0] Lcd_Write_Com(0x2c); // Mempry Write */ GPIOB_BASE->ODR = 0x8953; // Write_Com(0x2a); -Column Address Set //INV_DATA_MASK & ~LCD_WR & ~LCD_RS & ~LCD_CS | (0x2a << 3) /// (0010 1010 -> 1 0101 0000 = 0x150) => GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803; // считая, что x1 <= 240 | ((x1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((x1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803; // считая, что x2 <= 240 | ((x2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((x2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x895b; // Write_Com(0x2b); // Page Address Set GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((y1 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((y1 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((y2 & 0xff00)>>5); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0xa803 | ((y2 & 0xff) << 3); // GPIOB_BASE->BSRR = LCD_WR; GPIOB_BASE->ODR = 0x8963; // Write_Com(0x2c); // Mempry Write // 2c = 0010 1100 -> 1 0110 0000 -> 0x160 GPIOB_BASE->BSRR = LCD_WR; } void Lcd_Init(void) { GPIOB_BASE->BSRR = LCD_RESET; delay(5); GPIOB_BASE->BRR = LCD_RESET; delay(15); GPIOB_BASE->BSRR = LCD_RESET; delay(15); releaseLCD(); //GPIOB_BASE->BSRR = LCD_CS; delay(1); GPIOB_BASE->BSRR = LCD_WR; delay(1); selectLCD(); //GPIOB_BASE->BRR = LCD_CS; delay(1); Lcd_Write_Com(0xCB); Lcd_Write_Data(0x39); Lcd_Write_Data(0x2C); Lcd_Write_Data(0x00); Lcd_Write_Data(0x34); Lcd_Write_Data(0x02); Lcd_Write_Com(0xCF); Lcd_Write_Data(0x00); Lcd_Write_Data(0XC1); Lcd_Write_Data(0X30); Lcd_Write_Com(0xE8); Lcd_Write_Data(0x85); Lcd_Write_Data(0x00); Lcd_Write_Data(0x78); Lcd_Write_Com(0xEA); Lcd_Write_Data(0x00); Lcd_Write_Data(0x00); Lcd_Write_Com(0xED); Lcd_Write_Data(0x64); Lcd_Write_Data(0x03); Lcd_Write_Data(0X12); Lcd_Write_Data(0X81); Lcd_Write_Com(0xF6); // bytes order ????????????????????????????????? Lcd_Write_Data(0x01); Lcd_Write_Data(0x00); Lcd_Write_Data(0x00); // 0x00 Lcd_Write_Com(0xF7); Lcd_Write_Data(0x20); Lcd_Write_Com(0xC0); //Power control Lcd_Write_Data(0x23); //VRH[5:0] Lcd_Write_Com(0xC1); //Power control Lcd_Write_Data(0x10); //SAP[2:0];? BT[3:0] Lcd_Write_Com(0xC5); //VCOM control 1 Lcd_Write_Data(0x3e); // VMH[6] Contrast Lcd_Write_Data(0x28); // VML[6] Lcd_Write_Com(0xC7); //VCOM control 2 Lcd_Write_Data(0x86); // VMF[6] Lcd_Write_Com(0x36); // Memory Access Control Lcd_Write_Data(0x88); // MY=0, MX=1, MV=0, ML=0, BGR=1, MH=0, X=0, X=0 // было 0x48 Lcd_Write_Com(0x3A); // Pixel Format Set Lcd_Write_Data(0x55); // X=0, DPI[3]=101, X=0, DBI[3]=101 Lcd_Write_Com(0xB1); // Frame Control (in Normal Mode) Lcd_Write_Data(0x00); // X, X, X, X, X, X, DIVA[2]=00 (Division Rate = 0) Lcd_Write_Data(0x18); // X, X, X, RTNA[5]=11000 (Frame Rate = 79Hz) Lcd_Write_Com(0xB6); // Display Function Control Lcd_Write_Data(0x08); // X, X, X, X, PTG[2]=10, PT[2]=00 Lcd_Write_Data(0x82); // REV=1, GS=0, SS=0, SM=0, ISC[4]=0010 Lcd_Write_Data(0x27); // X, X, PCDIV[6]=100111 // Lcd_Write_Com(0xF6); // bytes order ????????????????????????????????? // Lcd_Write_Data(0x01); // Lcd_Write_Data(0x00); // Lcd_Write_Data(0x20); // 0x00 Lcd_Write_Com(0x11); //Exit Sleep // Sleep OUT delay(120); Lcd_Write_Com(0x29); //Display ON Lcd_Write_Com(0x2c); // Memory Write } #define PUTPIXEL { \ GPIOB_BASE->ODR = 0xa803 | ((c >> 5) & DATA_MASK); \ GPIOB_BASE->BSRR = LCD_WR; \ GPIOB_BASE->ODR = 0xa803 | ((c << 3) & DATA_MASK); \ GPIOB_BASE->BSRR = LCD_WR; \ } #define PUT_8_PIXELS { \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ PUTPIXEL \ } void H_line(unsigned int x, unsigned int y, unsigned int l, unsigned int c) { selectLCD(); // GPIOB_BASE->BRR = LCD_CS; Address_set(x, y, x+l-1, y); for(unsigned int i=0; i<l; i++) { PUTPIXEL } releaseLCD(); // GPIOB_BASE->BSRR = LCD_CS; } void V_line(unsigned int x, unsigned int y, unsigned int l, unsigned int c) { selectLCD(); // GPIOB_BASE->BRR = LCD_CS; Address_set(x, y, x, y+l-1); for(unsigned int i=0; i<l; i++) { PUTPIXEL } releaseLCD(); // GPIOB_BASE->BSRR = LCD_CS; } void Rect(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c) { H_line(x , y , w, c); H_line(x , y+h-1, w, c); V_line(x , y , h, c); V_line(x+w-1, y , h, c); } void Rectf(unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int c) { unsigned int i; for(i=0; i<w; i++) { V_line(x+i , y, h, c); } } void LCD_Clear(unsigned int c) { selectLCD(); // GPIOB_BASE->BRR = LCD_CS; Address_set(0,0,239,319); for(unsigned int i=0; i<(40*240); i++){ // для 2 пикселей за итерацию PUT_8_PIXELS } releaseLCD(); // GPIOB_BASE->BSRR = LCD_CS; } #endifА два остальных файла уже публиковались в сообщении №5.