2017年12月10日 星期日

OpenQCam樹莓派開源迷你相機#6_程式源碼

完整專案可直接到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_程式源碼

沒有留言:

張貼留言

【頂置】簡報、源碼、系列文快速連結區

常有人反應用手機瀏覽本部落格時常要捲很多頁才能找到系列發文、開源專案、課程及活動簡報,為了方便大家快速查詢,特整理連結如下,敬請參考! Edge AI Taiwan 邊緣智能交流區 全像顯示與互動交流區 台科大(NTUST) 人工智慧與邊緣運算實務 開南大學...