完整專案可直接到GITHUB下載,或者直接將下列內容複製到工作路徑執行。
// ********** ILI9341.h **********
// ********** ILI9341 LCD SPI 驅動程式標頭檔 **********
#include <bcm2835.h>
#define PIN_LCD_RESET RPI_V2_GPIO_P1_11
#define PIN_LCD_DC RPI_V2_GPIO_P1_13
#define PIN_LCD_LED RPI_V2_GPIO_P1_15
//#define PIN_LCD_MOSI PRI_V2_GPIO_P1_19
//#define PIN_LCD_MISO PRI_V2_GPIO_P1_21
//#define PIN_LCD_SCLK PRI_V2_GPIO_P1_23
//#define PIN_LCD_CS PRI_V2_GPIO_P1_24
#define PIN_GPIO_SW1 RPI_V2_GPIO_P1_40
#define PIN_GPIO_SW2 RPI_V2_GPIO_P1_37
#define PIN_GPIO_LED_R RPI_V2_GPIO_P1_35
#define PIN_GPIO_LED_G RPI_V2_GPIO_P1_33
int LCD_Inital(void);
int bcm2835_init(void);
void BCM2835_End(void);
int GPIO_Inital(void);
int SPI_Inital(void);
void LCD_SetReset(uint8_t);
void LCD_SetBacklight(uint8_t);
void LCD_SetMode(uint8_t);
void LCD_SetVerticalDisplay(void);
void LCD_SetHorizontalDisplay(void);
void LCD_HW_Reset(void);
void ILI9341_WriteCommand(char);
void ILI9341_WritePara(char);
void ILI9341_WriteData(char*,uint32_t);
void ILI9341_WriteLineBGR2RGB565(char*, uint32_t);
void ILI9341_SetWindow(uint16_t,uint16_t,uint16_t,uint16_t);
int ILI9341_Inital(void);
void ILI9341_Wakeup(void);
void ILI9341_Sleep(void);
// ********** ILI9341.cpp **********
// ********** ILI9341 LCD SPI 驅動程式 **********
#include "ILI9341.h"
// LCD module inital
int LCD_Inital(void)
{
if(!bcm2835_init()){ //若BCM2835庫初始化失敗則返回錯誤碼1
return 1;
}
if(!GPIO_Inital()){ //若BCM2835 GPIO初始化失敗則返回錯誤碼2
return 2;
}
if(!SPI_Inital()){ //若BCM2835 SPI初始化失敗則返回錯誤碼3
return 3;
}
LCD_SetBacklight(HIGH); //點亮LCD背光
LCD_HW_Reset(); //重置LCD
delay(50); //延時50ms
if(!ILI9341_Inital()){
LCD_SetBacklight(LOW); //若LCD初始化失敗則關閉背光
return 4; // 返回錯誤碼4
}
delay(300); //延時300ms
return 0;
}
// BCM2835 函式庫初始化
int BCM2835_Inital()
{
if(!bcm2835_init()){
return 0;
}
return 1;
}
// 關閉 BCM2835 庫
void BCM2835_End()
{
LCD_SetBacklight(LOW); //關閉LCD背光
bcm2835_spi_end(); //結束SPI
bcm2835_close();
}
// GPIO 初始化 設定接腳輸出入模式
int GPIO_Inital(void)
{
bcm2835_gpio_fsel(PIN_LCD_RESET, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(PIN_LCD_DC, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(PIN_LCD_LED, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(PIN_GPIO_SW1, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_fsel(PIN_GPIO_SW2, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_fsel(PIN_GPIO_LED_R, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(PIN_GPIO_LED_G, BCM2835_GPIO_FSEL_OUTP);
return 1;
}
// SPI 初始化
int SPI_Inital(void)
{
if(!bcm2835_spi_begin()){
return 0;
}
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // 最高位元先行
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // 模式0
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_16); // 256MHz/DIVIDER = 16 MHz for LCD SCLK
bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // 使用CE0_N
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // 低電位方式驅動CE0_N
return 1;
}
// 重置LCD模組
void LCD_SetReset(uint8_t state)
{
bcm2835_gpio_write(PIN_LCD_RESET, state);
}
// 設定LCD背光 0關閉 1開啟
void LCD_SetBacklight(uint8_t state)
{
bcm2835_gpio_write(PIN_LCD_LED, state);
}
// 設定LCD讀寫模式
// LOW(0x0): 命令模式; HIGH(0x1): 資料模式
void LCD_SetMode(uint8_t state)
{
bcm2835_gpio_write(PIN_LCD_DC, state);
}
// 設定LCD為垂直(240x320)顯示模式
void LCD_SetVerticalDisplay(void){
ILI9341_WriteCommand(0x36); // Memory Access Control
ILI9341_WritePara(0x48); // Row Address Order(MY), Column Address Order(MX), Top-Down refresh order, BGR Color Filter
ILI9341_SetWindow(0, 239, 0, 319);
}
// 設定LCD為水平(320x240)顯示模式
void LCD_SetHorizontalDisplay(void){
ILI9341_WriteCommand(0x36); // Memory Access Control
ILI9341_WritePara(0x28); // Row Address Order(MY), Column Address Order(MX), Top-Down refresh order, BGR Color Filter
ILI9341_SetWindow(0, 319, 0, 239);
}
// LCD硬體重置
void LCD_HW_Reset(void)
{
LCD_SetReset(HIGH);
delay(5);
LCD_SetReset(LOW);
delay(20);
LCD_SetReset(HIGH);
delay(150);
}
// 寫入 1 byte 命令到LCD模組
void ILI9341_WriteCommand(char cmd)
{
char buf = cmd;
LCD_SetMode(LOW); // set LCD module to command mode
bcm2835_spi_transfern(&buf,1); // write to SPI without read back
}
// 寫入 1 byte 資料到LCD模組
void ILI9341_WritePara(char para)
{
char buf = para;
LCD_SetMode(HIGH); // set LCD module to data mode
bcm2835_spi_transfern(&buf,1); // write to SPI without read back
}
// 連績寫入 n byte 資料到LCD模組
void ILI9341_WriteData(char *buf, uint32_t len)
{
LCD_SetMode(HIGH); // set data mode
bcm2835_spi_transfern(buf,len); // write to SPI without read back
}
// 轉換OPENCV BGR(24bit)資料格式變為RGB565(16bit)並傳輸到LCD模組
// BGR24bit:[B7][B6][B5][B4][B3][B2][B1][B0]|[G7][G6][G5][G4][G3][G2][G1][G0]|[R7][R6][R5][R4][R3][R2][R1][R0]
// RGB16bit:[R7][R6][R5][R4][R3][G7][G6][G5]|[G4][G3][G2][B7][B6][B5][B4][B3]|
void ILI9341_WriteLineBGR2RGB565(char *buf, uint32_t len)
{
char* pRGB565 = new char[len*2];
int posS = 0;
int posT = 0;
for(int i=0; i<len; i++){
posS += 3;
posT += 2;
pRGB565[ posT ] = (buf[posS+2] & 0xF8) | (buf[posS+1]>>5);
pRGB565[posT+1] = ((buf[posS+1] & 0x1C) << 3) | (buf[posS]>>3);
}
LCD_SetMode(HIGH); // set data mode
bcm2835_spi_transfern(pRGB565,len*2); // write to SPI without read back
}
// Set Display Window
void ILI9341_SetWindow(uint16_t sx, uint16_t ex, uint16_t sy, uint16_t ey)
{
ILI9341_WriteCommand(0x2A); // Column Address Set (X Axis)
ILI9341_WritePara(sx >> 8); // Start Column High Byte
ILI9341_WritePara(sx); // Start Column Low Byte
ILI9341_WritePara(ex >> 8); // End Column High Byte
ILI9341_WritePara(ex); // End Column Low Byte
ILI9341_WriteCommand(0x2B); // Page Address Set (Y Axis)
ILI9341_WritePara(sy >> 8); // Start Page High Byte
ILI9341_WritePara(sy); // Start Page Low Byte
ILI9341_WritePara(ey >> 8); // End Page High Byte
ILI9341_WritePara(ey); // End Page Low Byte
ILI9341_WriteCommand(0x2C); // Memory Write
}
// LCD module inital
int ILI9341_Inital()
{
ILI9341_WriteCommand(0xCB); // Power Control A
ILI9341_WritePara(0x39);
ILI9341_WritePara(0x2C);
ILI9341_WritePara(0x00);
ILI9341_WritePara(0x34); // vcore control, 1.6v
ILI9341_WritePara(0x02); // ddvdh control, 5.6v
ILI9341_WriteCommand(0xCF); // Power Control B
ILI9341_WritePara(0x00);
ILI9341_WritePara(0XC1); // power control
ILI9341_WritePara(0X30); // discharge path enable, enable ESD protection
ILI9341_WriteCommand(0xE8); // Driver Timing Control A
ILI9341_WritePara(0x85); // get driver non-overlap timing control, default + 1 unit
ILI9341_WritePara(0x00); // EQ and CR timing control, both default-1unit
ILI9341_WritePara(0x78); // pre-charge timing, default - 2 unit
ILI9341_WriteCommand(0xEA); // Driver Timing Control B
ILI9341_WritePara(0x00); // gate driver timing control
ILI9341_WritePara(0x00);
ILI9341_WriteCommand(0xED); // Power on Sequence Control
ILI9341_WritePara(0x64); // CP1 soft start keep 1 frame, CP23 soft start keep 3 frame
ILI9341_WritePara(0x03); // Power on sequence control, En_vcl 1st frame enable, En_ddvdh 4th frame enable
ILI9341_WritePara(0X12); // Power on sequence control, En_vgh 2nd frame enable, En_vgl 3rd frame enable
ILI9341_WritePara(0X81); // DDVDH enhance mode, enable
ILI9341_WriteCommand(0xF7); // Pump Ratio Control
ILI9341_WritePara(0x20); // Ratio control, DDVDH = 2xVCI
ILI9341_WriteCommand(0xC0); // Power control 1
ILI9341_WritePara(0x23); // Set the GVDD level, 4.60v
ILI9341_WriteCommand(0xC1); // Power control 2
ILI9341_WritePara(0x10); // Sets the factor used in the step-up circuits, DDVDH=VCI*2, VGH=VCI*7, VGL = -VCI*4
ILI9341_WriteCommand(0xC5); // VCOM Control
ILI9341_WritePara(0x3e); // Set VCOMH voltage, 4.200v
ILI9341_WritePara(0x28); // Set VCOML voltage, -0.700v
ILI9341_WriteCommand(0xC7); // VCOM control2
ILI9341_WritePara(0x86); // Set the CVOM offset voltage, VCOMH = VMH-58, VCOML = VML-58
ILI9341_WriteCommand(0x36); // Memory Access Control
ILI9341_WritePara(0x48); // Row Address Order(MY), Column Address Order(MX), Top-Down refresh order, BGR Color Filter
ILI9341_WriteCommand(0x3A); // COLMOD: Pixel Format Set
ILI9341_WritePara(0x55); // DPI and DBI are 16 bits / pixel
ILI9341_WriteCommand(0xB1); // Frame Rate Control (In Normal Mode)
ILI9341_WritePara(0x00); // DIVA setting, fosc / 1
ILI9341_WritePara(0x18); // RTNA setting, Frame rate 79 Hz
ILI9341_WriteCommand(0xB6); // Display Function Control
ILI9341_WritePara(0x08); // Non-display area interval scan, determine source in partial display mode
ILI9341_WritePara(0x82); // REV normally white, GS SM G1->G320, SS S1-S720, ISC 5 frames 85ms
ILI9341_WritePara(0x27); // LCD driver line 320 lines
ILI9341_WriteCommand(0xF2); // Enable 3G
ILI9341_WritePara(0x00); // disable 3 gamma control
ILI9341_WriteCommand(0x26); // Gamma Set
ILI9341_WritePara(0x01); // Gamma curve 1 (G2.2)
ILI9341_WriteCommand(0xE0); // Positive Gamma Correction
ILI9341_WritePara(0x0F);
ILI9341_WritePara(0x31);
ILI9341_WritePara(0x2B);
ILI9341_WritePara(0x0C);
ILI9341_WritePara(0x0E);
ILI9341_WritePara(0x08);
ILI9341_WritePara(0x4E);
ILI9341_WritePara(0xF1);
ILI9341_WritePara(0x37);
ILI9341_WritePara(0x07);
ILI9341_WritePara(0x10);
ILI9341_WritePara(0x03);
ILI9341_WritePara(0x0E);
ILI9341_WritePara(0x09);
ILI9341_WritePara(0x00);
ILI9341_WriteCommand(0XE1); // Negative Gamma Correction
ILI9341_WritePara(0x00);
ILI9341_WritePara(0x0E);
ILI9341_WritePara(0x14);
ILI9341_WritePara(0x03);
ILI9341_WritePara(0x11);
ILI9341_WritePara(0x07);
ILI9341_WritePara(0x31);
ILI9341_WritePara(0xC1);
ILI9341_WritePara(0x48);
ILI9341_WritePara(0x08);
ILI9341_WritePara(0x0F);
ILI9341_WritePara(0x0C);
ILI9341_WritePara(0x31);
ILI9341_WritePara(0x36);
ILI9341_WritePara(0x0F);
ILI9341_Wakeup();
return 1;
}
// LCD module exit sleep mode
void ILI9341_Wakeup(void)
{
ILI9341_WriteCommand(0x11); // Sleep Out
delay(120); // delay 120ms, must be > 5 ms
ILI9341_WriteCommand(0x29); // Display On
ILI9341_WriteCommand(0x2C); // Memory Write
}
//LCD module into sleep mode
void ILI9341_Sleep(void)
{
ILI9341_WriteCommand(0x28); // Display Off
delay(20); // delay 20ms
ILI9341_WriteCommand(0x10); // Enter Sleep Mode
}
// ********** main.cpp **********
// ********** 主程式 **********
#include <stdio.h>
#include <iostream>
#include "ILI9341.h"
#include <opencv2/opencv.hpp>
#include <ctime>
using namespace std;
using namespace cv;
int main(int argc, char **argv)
{
double t0,t1,t2;
bool bPress = false; // 記錄快門按下狀態
int result = LCD_Inital();
switch(result){ // 顯示初始化錯誤訊息
case 0:
printf("LCD inital OK.\n");
break;
case 1:
printf("BCM2835 inital failed. Are you running as root??\n");
break;
case 2:
printf("GPIO inital failed. Are you running as root??\n");
break;
case 3:
printf("SPI inital failed. Are you running as root??\n");
break;
case 4:
printf("ILI9341 inital failed. Please check PCB connection is OK.\n");
break;
default:
printf("LCD inital failed.\n");
}
if(result != 0)
return 0;
VideoCapture cap(0); // 啟動攝像頭連續取像
if (!cap.isOpened()) { // 若無法開啟則結束
cerr << "ERROR: Unable to open the camera" << endl;
return 0;
}
cap.set(CV_CAP_PROP_FRAME_WIDTH, 320); // 設定攝像頭輸入解析度為320*240
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);
Mat frame;
cout << "Start grabbing !" << endl;
LCD_SetHorizontalDisplay(); // 設定LCD為橫式顯示
char strFps[10]; // 儲存速度字串
t1 = (double)getTickCount(); // 取得目前時間
while(1) {
t0 = t1; // 儲存舊時間
t1 = (double)getTickCount(); // 儲存目前時間
// 若快門鍵(SW1)按下則閃光燈(LED1)點亮
if(bcm2835_gpio_lev(PIN_GPIO_SW1)!=0){
bPress = true;
bcm2835_gpio_write(PIN_GPIO_LED_R, 1); // 開啟閃光燈
delay(30); // 等待取到有打光的影像
}
cap >> frame; // 將取得的影像複製到frame
// 計算兩次執行時間差 取倒數即為每秒幀數 顯示在LCD左上角
// 若不需顯示則註解掉下面兩行
sprintf(strFps,"%2.1f FPS", 1.0 / ((t1-t0) / getTickFrequency()));
putText(frame, strFps, Point(20,40), FONT_HERSHEY_DUPLEX, 0.8, Scalar(0,255,0),1);
// // 測試按鍵SW1 SW2是否正常 並顯示在LCD上
// if(bcm2835_gpio_lev(PIN_GPIO_SW1)!=0)
// putText(frame, "SW1 ON ", Point (20,70), FONT_HERSHEY_DUPLEX,1, Scalar(0,255,0),1);
// else
// putText(frame, "SW1 OFF", Point (20,70), FONT_HERSHEY_DUPLEX,1, Scalar(0,255,0),1);
// if(bcm2835_gpio_lev(PIN_GPIO_SW2)!=0)
// putText(frame, "SW2 ON ", Point (20,100), FONT_HERSHEY_DUPLEX,1, Scalar(0,255,0),1);
// else
// putText(frame, "SW2 OFF", Point (20,100), FONT_HERSHEY_DUPLEX,1, Scalar(0,255,0),1);
// 將取得影像逐行顯示在LCD上
for(int i = 0; i < frame.rows; i++){
char *ptrS = frame.ptr<char>(i);
ILI9341_WriteLineBGR2RGB565(ptrS, frame.cols);
}
// 若無法取得影像則結束程式
if (frame.empty()) {
cerr << "ERROR: Unable to grab from the camera" << endl;
break;
}
// 若快門鍵有按過則清除按下旗標 關閉閃光燈 並存檔
if(bPress){
bPress = false;
bcm2835_gpio_write(PIN_GPIO_LED_R, 0); // 關閉閃光燈
char file_name[30];
time_t now = time(0);
tm *ltm = localtime(&now);
sprintf(file_name,"%02d%02d%02d.jpg",1+ltm->tm_hour,1+ltm->tm_min,1+ltm->tm_sec); // 以時間當作檔名
imwrite(file_name,frame); // 儲存影像
delay(1000); // 畫面暫停一秒
}
// 若SW2按下則綠燈閃一下結束程式
if(bcm2835_gpio_lev(PIN_GPIO_SW2) != 0){
bcm2835_gpio_write(PIN_GPIO_LED_G, 1);
delay(50);
bcm2835_gpio_write(PIN_GPIO_LED_G, 0);
break;
}
}
cap.release();
BCM2835_End();
cout << "Done!" <<endl;
return 0;
}
// *********** go.sh **********
// *********** 編譯指令 **********
clear
date
g++ `pkg-config --libs opencv` main.cpp ILI9341.cpp -o camera -l bcm2835
date
OpenQCam樹莓派開源迷你相機 (專案首頁)
OpenQCam樹莓派開源迷你相機#1_硬體需求
OpenQCam樹莓派開源迷你相機#2_硬體線路圖
OpenQCam樹莓派開源迷你相機#3_作業系統安裝
OpenQCam樹莓派開源迷你相機#4_工作環境建置
OpenQCam樹莓派開源迷你相機#5_系統開發
OpenQCam樹莓派開源迷你相機#6_程式源碼
OmniXRI (Omni-eXtened Reality Interaction) 歐尼克斯實境互動工作室是一個全方位電腦視覺、實境互動、邊緣計算及人工智慧技術的愛好者及分享者,歡迎大家不吝留言指教多多交流。 這裡的文章大部份是個人原創,如有引用到他人內容都會註明,若不慎疏漏侵犯到,煩請儘快通知,我們會儘速處理。當然這裡的資料也歡迎引用及推廣,煩請註明出處(網頁網址),謝謝!
訂閱:
張貼留言 (Atom)
【頂置】簡報、源碼、系列文快速連結區
常有人反應用手機瀏覽本部落格時常要捲很多頁才能找到系列發文、開源專案、課程及活動簡報,為了方便大家快速查詢,特整理連結如下,敬請參考! Edge AI Taiwan 邊緣智能交流區 全像顯示與互動交流區 台科大(NTUST) 人工智慧與邊緣運算實務 開南大學...
-
作者:Jack OmniXRI, 2024/10/15 留言 相信有在玩AI影像「物件偵測」的朋友對於YOLO(Yolo Only Look Once)一定不會陌生。從2015年第一版(v1)問世至今,在各路大神的努力之下,現在已發展到第十一版(v11),而其中 v4, v7...
-
作者: Jack OmniXRI 2024/10/31 今天剛好看了一個有趣的新聞[1]提到「 面試官問 3, 4, 5 組成最大數字是多少? 博士神回答妙錄取 」,結果答案是「5的43次方 5 43 = 1.136 ∗ 10 30 」,當然這是不用計算機的情況下最快的反...
沒有留言:
張貼留言