看完摄像头的寄存器之后,就开始配置摄像头的DMA采集。
还是看网上的大神截图,摄像头的时序。
从中我们可以看出,在行信号之后开始采集PCLK的图像信息。所以我们用PCLK的上升沿触发DMA进行图像采集,DMA目的地址默认加一这样就能实现图像的自动采集,每次行中断过来进行DMA目的地址的修正,具体下面的代码。
// MT9V034 Port Init
void MT_Init(void)
{
uint16_t data = 0;
EXTI_Init(GPIOD,13,rising_up); //行中断
EXTI_Init(GPIOD,14,falling_up); //场中断
GPIO_Init(GPIOD,0,GPI,0); //八位数据输入口
GPIO_Init(GPIOD,1,GPI,0);
GPIO_Init(GPIOD,2,GPI,0);
GPIO_Init(GPIOD,3,GPI,0);
GPIO_Init(GPIOD,4,GPI,0);
GPIO_Init(GPIOD,5,GPI,0);
GPIO_Init(GPIOD,6,GPI,0);
GPIO_Init(GPIOD,7,GPI,0);
SCCB_Init(); //初始化IIC IO口
if(SCB_RegRead(0x5C,MT_VERSION,&data) == 0)//读取摄像头版本寄存器
{
if(data != VERSION) //版本号不对说明出错
{
while(1);
}
}
else
{
while(1);
}
SCB_RegWrite(MT_I2C_ADDR,MT_Mode,0x30a); //设置摄像头图像4*4分频输出PCLK 27/4 = 6.75M
/*下面的三个配置可以提高图像的稳定性*/
SCB_RegWrite(MT_I2C_ADDR,0x20,0x3C7);
SCB_RegWrite(MT_I2C_ADDR,0x2B,0x003);
SCB_RegWrite(MT_I2C_ADDR,0x2F,0x003);
DMACaptureInit(DMA_CH1,(void*)&PTD_BYTE0_IN,(void*)Image_Data1,PTD12,DMA_BYTE1,Image_Width,DMA_rising_down);//初始化DMA采集
Image_Data1_Status = Captrueing;
Image_Data2_Status = INIT;
}
volatile u8 Image_Data1[Height][Width];
volatile u8 Image_Data2[Height][Width];
u16 Image_Width = Width;//窗口宽度
u16 Image_Height= Height;//窗口高度
/* 状态机 */
typedef enum
{
INIT,
Captrueing,
Finish,
Using
}MT9V034_Status;
MT9V034_Status Image_Data1_Status;
MT9V034_Status Image_Data2_Status;
//摄像头图像采集中断处理函数
void PORTD_IRQHandler(void)
{
static uint32_t h_counter=0;
//行中断PTD13
if((PORTD_ISFR & 0x2000))//行中断 (1<<13)
{
PORTD_ISFR |= 0x2000; //清除中断标识
if(h_counter > Image_Height)
{
h_counter=0;
}
if(Image_Data1_Status == Captrueing||Image_Data1_Status == Finish)
{
DMA_SetDestAddress(DMA_CH1,(uint32_t)(&Image_Data1[h_counter++][0]));
}
else if(Image_Data2_Status == Captrueing||Image_Data2_Status == Finish)
{
DMA_SetDestAddress(DMA_CH1,(uint32_t)(&Image_Data2[h_counter++][0]));
}
DMA_SetMajorLoopCounter(DMA_CH1,Image_Width);
DMA_EN(DMA_CH1);
return ;
}
//场中断PTD14
if((PORTD_ISFR & 0x4000))//(1<<14)
{
PORTD_ISFR |= 0x4000; //清除中断标识
// 用户程序
if(Image_Data1_Status == Captrueing)
{
if(Image_Data2_Status != Using)
{
Image_Data1_Status = Finish;
Image_Data2_Status = Captrueing;
}
}
else if(Image_Data2_Status == Captrueing)
{
if(Image_Data1_Status != Using)
{
Image_Data2_Status = Finish;
Image_Data1_Status = Captrueing;
}
}
else
{
}
h_counter = 0;
return ;
}
}
/***************************************************************
* 河南科技大学一队
*
* 函数名称:void DMACaptureInit(DMA_CHn CHn,void *SADDR, void *DADDR,PTXn_e ptxn,DMA_BYTEn byten,u32 count,DMA_PORTx2BUFF_cfg cfg)
* 功能说明:初始化DMA
* 参数说明:
* 函数返回:读取字节
* 修改时间:2018年3月5日
* 备 注:
***************************************************************/
void DMACaptureInit(DMA_CHn CHn,void *SADDR, void *DADDR,PTXn_e ptxn,DMA_BYTEn byten,u32 count,DMA_Count_cfg cfg)
{
u8 BYTEs = (byten == DMA_BYTE1 ? 1 : (byten == DMA_BYTE2 ? 2 : (byten == DMA_BYTE4 ? 4 : 16 ) ) ); //计算传输字节数
//开启时钟
SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; //打开DMA模块时钟
SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK; //打开DMA多路复用器时钟
// 配置 DMA 通道 的 传输控制块 TCD ( Transfer Control Descriptor )
DMA_SADDR(CHn) = (u32)SADDR; // 设置 源地址
DMA_DADDR(CHn) = (u32)DADDR; // 设置目的地址
DMA_SOFF(CHn) = 0; // 设置源地址不变
DMA_DOFF(CHn) = BYTEs; // 每次传输后,目的加BYUEs
DMA_ATTR(CHn) = (0
| DMA_ATTR_SMOD(0x0) // 源地址模数禁止 Source address modulo feature is disabled
| DMA_ATTR_SSIZE(byten) // 源数据位宽 :DMA_BYTEn 。 SSIZE = 0 -> 8-bit ,SSIZE = 1 -> 16-bit ,SSIZE = 2 -> 32-bit ,SSIZE = 4 -> 16-byte
| DMA_ATTR_DMOD(0x0) // 目标地址模数禁止
| DMA_ATTR_DSIZE(byten) // 目标数据位宽 :DMA_BYTEn 。 设置参考 SSIZE
);
DMA_CITER_ELINKNO(CHn) = DMA_CITER_ELINKNO_CITER(count); //当前主循环次数
DMA_BITER_ELINKNO(CHn) = DMA_BITER_ELINKYES_BITER(count);//起始主循环次数
DMA_CR &= ~DMA_CR_EMLM_MASK; // CR[EMLM] = 0 disable Minor Loop Mapping
DMA_NBYTES_MLNO(CHn) = DMA_NBYTES_MLNO_NBYTES(BYTEs); // 通道每次传输字节数,这里设置为BYTEs个字节。注:值为0表示传输4GB
// 配置 DMA 传输结束后的操作
DMA_SLAST(CHn) = 0; //调整源地址的附加值,主循环结束后恢复源地址
DMA_DLAST_SGA(CHn) = 0; //调整目的地址的附加值,主循环结束后恢复目的地址或者保持地址
DMA_CSR(CHn) = (0
| DMA_CSR_DREQ_MASK //主循环结束后停止硬件请求
| DMA_CSR_INTMAJOR_MASK //主循环结束后产生中断
);
// 配置 DMA 触发源
DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR, CHn) = (0
| DMAMUX_CHCFG_ENBL_MASK // Enable routing of DMA request
| DMAMUX_CHCFG_SOURCE((ptxn >> 5) + DMA_Port_A) // 通道触发传输源:
);
SIM_SCGC5 |= (SIM_SCGC5_PORTA_MASK << (ptxn>>5)); //开启PORTx端口
GPIO_PDDR_REG(GPIOX[(ptxn>>5)]) &= ~(1 << (ptxn & 0x1f)); //设置端口方向为输入
PORT_PCR_REG(PORTX[(ptxn>>5)], (ptxn & 0x1F)) = ( 0
| PORT_PCR_MUX(1) // 复用GPIO
| PORT_PCR_IRQC(cfg & 0x03 ) // 确定触发模式
| ((cfg & 0xc0 ) >> 6) // 开启上拉或下拉电阻,或者没有
);
GPIO_PDDR_REG(GPIOX[(ptxn>>5)]) &= ~(1 << (ptxn && 0x1F));
//开启中断
DMA_EN(CHn); //使能通道CHn 硬件请求
//DMA_IRQ_EN(CHn); //允许DMA通道传输
}
/***************************************************************
* 河南科技大学一队
*
* 函数名称:void DMA_SetDestAddress(uint8_t ch,uint32_t address)
* 功能说明:设置DMA传输的目的地址
* 参数说明:
* 函数返回:读取字节
* 修改时间:2018年3月5日
* 备 注:
***************************************************************/
void DMA_SetDestAddress(uint8_t CHn,uint32_t address)
{
DMA_DADDR(CHn) = address; // 设置目的地址
}
/***************************************************************
* 河南科技大学一队
*
* 函数名称:void DMA_SetMajorLoopCounter(uint8_t CHn,uint32_t Val)
* 功能说明:设置传输循环次数
* 参数说明:
* 函数返回:void
* 修改时间:2018年3月5日
* 备 注:
***************************************************************/
void DMA_SetMajorLoopCounter(uint8_t CHn,uint32_t Val)
{
DMA_CITER_ELINKNO(CHn) = DMA_CITER_ELINKNO_CITER(Val); //当前主循环次数
DMA_BITER_ELINKNO(CHn) = DMA_BITER_ELINKYES_BITER(Val);//起始主循环次数
}
这样就行实现图像的采集,但是图像还是有一个黑边,可能和DMA的频率有关KV58的DMA最高频率只有27.5MHZ,虽然我把MT9V034的频率降低到了6.75MHZ,但是采集的余量还不是很大,在使能DMA之后就要进行第一个像素点的采集,有对不齐的现象出现。
DMA时钟配置图,