PIC32MX + OV7670カメラ　カラーキャプチャー

# OV7670カラー識別トラッキング
:::note warn
24.11.11 ピン配表　訂正：ST7735SのA０ピン
:::

<iframe width="560" height="315" src="https://www.youtube.com/embed/MJpUOP4pbnQ?si=NPhOdRMttjZmPr0p" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

![IMG_5076.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/e5229eec-ae22-f277-e832-e452d09f590c.jpeg)

画面の中の、赤い紙がある場所を特定し、緑丸を表示させています。

:::note
# カラーキャプチャー構成概略
![概略図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/8eecb69f-cf50-9f5a-9671-605be0e41acd.jpeg)
ポートEの８ビットパラレルのピクセルデータをDMA1で受信して、RAMのエリアに転送します。RAMエリアは、１画面分、160×120×2バイトを確保しています。

カメラからのトリガーパルスPCLKに同期して、この8ビットパラレルデータはPORTEを介して、DMA1が自動的にRAMに転送し、用意した配列に格納されます。

データが160×120×2バイトに達すると、DMA2割込みが発生し、そのDMA2割込み処理関数内で、カラーキャプチャー処理を行い、直後に、DMA2によるSPI転送で、液晶にキャプチャーの結果とカメラ画像を表示させています。

一画面全部を一気にRAMに展開し、かつカラーキャプチャー処理を行ったので、FPSは低下しています。
:::

# PIC32MX　ピン割り付け表
![ピン配改２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/0ae83750-60a8-5a87-a456-f67626b96182.png)


:::note
# コード　DMAの初期化
転送元、転送先の指定。その大きさ。転送するサイズ。DMA転送のトリガーとなる割込みイベントの指定。
転送完了時の割込み処理関数での処理の記述がポイント。
マスターしてしまえば、とても高速で便利。

```C:DMA1,2初期化
//DMA2-------------------------------------------------
//sprite1（RAM配列）からSPI2へ転送
void DMA2Init(void)
{
    IEC2bits.DMA2IE = 0;   // 設定のためDMA2を一時停止させる
   
    DCH2CON = 0;
    DCH2ECON = 0;
    DCH2INT = 0;

    DCH2CONbits.CHPRI = 3;              // DMAチャネルの優先度(3)
    DCH2ECONbits.CHSIRQ = _TIMER_2_IRQ;   // DMAを開始する割り込み番号
    DCH2ECONbits.SIRQEN = 1;            // 割り込みによる転送を有効にする
    DCH2INTbits.CHBCIE = 1;             // Channel Block Transfer Complete Interrupt Flag bit
    
    DCH2SSA = KVA_TO_PA(&sprite1);
    //DCH2SSIZ = 128*160*3;
    DCH2SSIZ = 160*120*2;
    DCH2DSA = KVA_TO_PA(&SPI2BUF);
    DCH2DSIZ = 1;
    DCH2CSIZ = 1;
    
    IFS2bits.DMA2IF = 0;   // 割り込みフラグのクリア
    IPC11bits.DMA2IP = 7;  // 割り込み優先度
    IPC11bits.DMA2IS = 3;  // 副割り込み優先度
    IEC2bits.DMA2IE = 1;   // DMA2割り込みを有効にする
    DCH2CONbits.CHEN = 1;               // DMA1を使用する
    DMACONbits.ON = 0;     // DMAモジュールを有効にする
}

//DMA1-------------------------------------------------
//PORTEからsprite1(RAM配列)へ転送
void DMA1Init(void)
{
    IEC2bits.DMA1IE = 0;   // 設定のためDMA2を一時停止させる
   
    DCH1CON = 0;
    DCH1ECON = 0;
    DCH1INT = 0;

    DCH1CONbits.CHPRI = 3;              // DMAチャネルの優先度(3)
    DCH1ECONbits.CHSIRQ = _EXTERNAL_1_IRQ;   // DMAを開始する割り込み番号
    DCH1ECONbits.SIRQEN = 1;            // 割り込みによる転送を有効にする
    DCH1INTbits.CHDDIE = 1;             // Channel Block Transfer Complete Interrupt Flag bit
     
    DCH1SSA = KVA_TO_PA(&PORTE);
    DCH1SSIZ = 1;
    DCH1DSA = KVA_TO_PA(&sprite1);
    DCH1DSIZ = 160*120*2;
    DCH1CSIZ = 1;
    
    IFS2bits.DMA1IF = 0;   // 割り込みフラグのクリア
    IPC10bits.DMA1IP = 7;  // 割り込み優先度
    IPC10bits.DMA1IS = 3;  // 副割り込み優先度
    IEC2bits.DMA1IE = 1;   // DMA2割り込みを有効にする
    DCH1CONbits.CHEN = 1;               // DMA1を使用する
    DMACONbits.ON = 0;     // DMAモジュールを有効にする
}
```
:::

:::note 
# DMA割込み処理
```C:DMA ISR.c
//DMA2
//トリガー:Timer2
//from sprite1 to SPI2BUF
//sprite1 size:160*2
//SPI2BUF size:1
void __ISR(_DMA2_VECTOR, IPL7AUTO) ISR_Dma2(void)
{     
    IFS2bits.DMA2IF = 0;        // DMA2割り込みフラグのクリア
    if(DCH2INTbits.CHBCIF == 1)
    {
        SPI2_Stop();
        T2CONbits.ON=0;
        //LATBbits.LATB1=~LATBbits.LATB1;
        DCH2SSA = KVA_TO_PA(&sprite1);
        
        DCH2CONbits.CHEN = 1;   // DMA2を有効にする
        
        DCH2INTbits.CHBCIF = 0; // ブロック終了割り込みフラグのクリア
        
        DCH2ECONbits.SIRQEN = 1;
    }
    
}

//DMA1
//トリガー:外部割込み１ INT1
//from PORTE(size 1)
//to sprite1(size160*2)
void __ISR(_DMA1_VECTOR, IPL7AUTO) ISR_Dma1(void)
{
    static int i;
    IFS2bits.DMA1IF=0;
    if(DCH1INTbits.CHDDIF==1)
    {
        DCH1INTbits.CHDDIF=0;
        
        
        DMACONbits.ON = 0; 
        colorCap();//カラーキャプチャー検索、位置表示。
        
        pushSprite();//この関数の中で、Timer2を起動。連動してＤＭＡ２が起動。
        
        DCH1DSA = KVA_TO_PA(&sprite1);
        DCH1CONbits.CHEN=1;
        
        //IEC2bits.DMA1IE = 0;
        //IEC2bits.DMA2IE = 0;
    }
}
```
:::

:::note
# カラーキャプチャー
1画面全体の中から、しきい値条件に当てはまったピクセル位置（行と列）を検索。しきい値条件を超えた点が5個になったら、検索終了します。1画面全体を一回で検索すると、次のカメラデータのページに間に合わないので、10行ずつ調べています。120行あるので、12フレーム画面更新で、全体から位置を特定しています。
　なので、高速で移動した場合、追尾できません。対象物をゆっくり動かすと、なんとか、トレースしてくれます。
 画面上に対象物がない場合、緑の丸の表示は消えます。
やはり、システムクロックは200Mhzぐらいほしいところです。
```C:カラーキャプチャーのコード
void colorCap(void)
{
    int col,row,pix,err;
    static uint8_t xmin, xmax, length;
    uint8_t color,red,green;
    static int row1=20;
    bool exit;
    static bool exist;
    static int no_exist=0;
    int counter;
    
    exit = false;
    counter=0;
    for(row=row1; row<row1+10; row++)
    {
        for(pix=10; pix<300; pix++)
        {
            color = sprite1[(row*320)+pix];
            if(pix%2==0)
            {
                red = color>>3;
                green = (color&0x07)<<3;
            
                if(red>20 & green<10)
                {
                    col = pix/2; 
                    OV7670.cCap.x.min = col;
                    OV7670.cCap.y.min = row;
                    counter++;
                    if(counter>5)
                    {
                        exit=true;
                        exist = true;
                        break;
                    }
                }   
            }
            if(exit)break;
        }
    }
    
    if((row==row1+10) && (pix==300))
        no_exist++;
    if(no_exist>=10)
    {
        no_exist=0;
        exist=false;
    }
    
    row1+=10;
    if(row1>=110)
    {
        row1=20;
    }
    
    if(exist && OV7670.cCap.x.min!=0 && OV7670.cCap.y.min!=0)
    {
        bresenham_circle(OV7670.cCap.x.min, OV7670.cCap.y.min, 10, 0x0f, 0x3F, 0x00);
        bresenham_circle(OV7670.cCap.x.min, OV7670.cCap.y.min,  9, 0x0f, 0x3F, 0x00);
    }
    
    
}
:::