6 - 输入/输出:DMA

DMA, Direct Memory Access. 设定后,无需CPU干预即可将外设数据读到RAM中去。
摄像头组需要不断读取图像到内存,DMA可以分担图像读取的操作,大大减少了CPU的负担,于是我们可以有更多资源处理其他事情。
DMA另一个好处是可以跟着摄像头的PCLK读取数据,图像不会有噪点。
坏消息是,XS128不带DMA功能,呵呵。

通过DMA读取图像的方法:
1. 初始化摄像头管脚输入方向,初始化DMA;
2. 场中断到来,允许行中断;
3. 行中断到来,开启DMA,让DMA读取一行的数据;
4. 关闭DMA,等待下一次行中断。


1. 初始化管脚输入方向

    gpio_init(CCD_PORT,CCD0,GPI,0);
    gpio_init(CCD_PORT,CCD1,GPI,0);
    gpio_init(CCD_PORT,CCD2,GPI,0);
    gpio_init(CCD_PORT,CCD3,GPI,0);
    gpio_init(CCD_PORT,CCD4,GPI,0);
    gpio_init(CCD_PORT,CCD5,GPI,0);
    gpio_init(CCD_PORT,CCD6,GPI,0);
    gpio_init(CCD_PORT,CCD7,GPI,0);

2. 初始化行场中断(外部中断)

    //行中断
    FTM_Input_init(CCD_FTM,CCD_HR_CH,Rising);              //初始化FTM输入捕捉模式,上升沿触发
    //场中断
    FTM_Input_init(CCD_FTM,CCD_VS_CH,Falling);             //初始化FTM输入捕捉模式,下降沿触发

3. 初始化DMA
具体方法请参考野火手册。

    DMA_PORTx2BUFF_Init (CCD_DMA_CH,(void *)&CCD_DATA,&Pic_Buffer[0][0],PLCK_CH,DMA_BYTE1,COLUMN,DMA_rising);//DMA_falling DMA_rising
    //DMA通道4初始化,PTA27上升沿触发DMA传输,源地址为PTD_BYTE0_IN,目的地址为:BUFF ,每次传输1Byte,传输3次后停止传输,恢复目的地址   
    /* 开启中断 */
    //DMA_EN(CCD_DMA_CH);                                    //使能通道CHn 硬件请求
    //DMA_IRQ_EN(CCD_DMA_CH);                                //允许DMA通道传输 
    DMA_IRQ_CLEAN(CCD_DMA_CH);

4. 行场中断处理

/************************************************************************* 
*  函数名称:PORTB_IRQHandler
*  功能说明:摄像头行场中断服务函数
*  参数说明:无   
*  函数返回:无
*  修改时间:2014-3-29 modified by feichashao
*  备    注:
            < 使用了往届学长的代码 >
             原本杨向军的摄像头例程使用的是FTM模块完成行场中断任务,
             这里由于直立车的测速模块已经占用了FTM模块的两个子模块通道,
             因此这里我改用外部中断EXTI模块完成行场中断任务,
             当然也有人说建议使用FTM进行摄像头终端,但原因我也不太明白
*************************************************************************/
u32 tmpline = 0;//行号
u32 error_row = 0;//发生错误行号
u32 tmprow = 0; //列号
u32 tmpnum = 0; //场号
u32 picLineSum = 0;//总行数

void PORTB_IRQHandler()
{
     u32 tmpISFR = 0;
     tmpISFR=PORTB_ISFR;
     PORTB_ISFR |= 0xFFFFFFFF;
     
     // DisableInterrupts; //注释掉,以允许其他中断进入
     //========================== n = CAM_HR_CHn ========================================
     
     if((tmpISFR & (1<<CAM_HR_CHn)))   //PB1 触发中断  行中断
     {
         tmpline++; 

         if(0 == (tmpline % 2))//对tmpline取余,当tmpline为偶数行时进入(进行数据传输)
         {

             if(DMA_CITER_ELINKNO(CAM_DMA_CH) != COLUMN)
             {
               //uartPrintf("DMA末传输结束 %d \n",DMA_CITER_ELINKNO(CCD_DMA_CH));
                 Error = 1;
                 error_row = tmprow ;
             }
             
             DMA_SETDAADDR(CAM_DMA_CH,&Pic_Buffer[tmprow][0]);//Pic_Buffer的大小为120(row)*160(column) 这里是指从第row行的第0列(个像素点)开始传输
             DMA_EN(CAM_DMA_CH);//使能通道硬件DMA请求
             
             tmprow++;
             
             if(tmprow >= ROW)
             {
                 photeCompleteFlag = 1;
             }
             
         }
     }
     //========================== n = CAM_VS_CHn ========================================
     if((tmpISFR & (1<<CAM_VS_CHn))) //PTB2 触发中断  场中断;
     {
         tmpnum++;     
         picLineSum = tmpline;
         tmpline = 0;//行号清零
         tmprow = 0; //列号清零
         DMA_IRQ_CLEAN(CAM_DMA_CH);//清除通道传输中断标志位
         En_HREF;                  //打开行中断    
     }
     
     if(!(tmpISFR & (1<<CAM_HR_CHn)) && !(tmpISFR & (1<<CAM_VS_CHn)))
     {
         uartPrintf("Unexpected Interrapt 0x%X",tmpISFR);
     }
    // EnableInterrupts; //注释掉,以允许其他中断进入
}