215 lines
8.7 KiB
C
215 lines
8.7 KiB
C
#include "sys.h"
|
||
#include "dcmi.h"
|
||
#include "lcd.h"
|
||
#include "ltdc.h"
|
||
#include "led.h"
|
||
#include "ov5640.h"
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
|
||
//ALIENTEK STM32H7开发板
|
||
//DCMI驱动代码
|
||
//正点原子@ALIENTEK
|
||
//技术论坛:www.openedv.com
|
||
//创建日期:2017/8/15
|
||
//版本:V1.0
|
||
//版权所有,盗版必究。
|
||
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
|
||
//All rights reserved
|
||
//////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DCMI_HandleTypeDef DCMI_Handler; //DCMI句柄
|
||
DMA_HandleTypeDef DMADMCI_Handler; //DMA句柄
|
||
|
||
u8 ov_frame=0; //帧率
|
||
extern void jpeg_data_process(void); //JPEG数据处理函数
|
||
|
||
//DCMI初始化
|
||
void DCMI_Init(void)
|
||
{
|
||
DCMI_Handler.Instance=DCMI;
|
||
DCMI_Handler.Init.SynchroMode=DCMI_SYNCHRO_HARDWARE; //硬件同步HSYNC,VSYNC
|
||
DCMI_Handler.Init.PCKPolarity=DCMI_PCKPOLARITY_RISING; //PCLK 上升沿有效
|
||
DCMI_Handler.Init.VSPolarity=DCMI_VSPOLARITY_LOW; //VSYNC 低电平有效
|
||
DCMI_Handler.Init.HSPolarity=DCMI_HSPOLARITY_LOW; //HSYNC 低电平有效
|
||
DCMI_Handler.Init.CaptureRate=DCMI_CR_ALL_FRAME; //全帧捕获
|
||
DCMI_Handler.Init.ExtendedDataMode=DCMI_EXTEND_DATA_8B; //8位数据格式
|
||
HAL_DCMI_Init(&DCMI_Handler); //初始化DCMI,此函数会开启帧中断
|
||
|
||
//关闭行中断、VSYNC中断、同步错误中断和溢出中断
|
||
//__HAL_DCMI_DISABLE_IT(&DCMI_Handler,DCMI_IT_LINE|DCMI_IT_VSYNC|DCMI_IT_ERR|DCMI_IT_OVR);
|
||
|
||
//关闭所有中断,函数HAL_DCMI_Init()会默认打开很多中断,开启这些中断
|
||
//以后我们就需要对这些中断做相应的处理,否则的话就会导致各种各样的问题,
|
||
//但是这些中断很多都不需要,所以这里将其全部关闭掉,也就是将IER寄存器清零。
|
||
//关闭完所有中断以后再根据自己的实际需求来使能相应的中断。
|
||
DCMI->IER=0x0;
|
||
|
||
__HAL_DCMI_ENABLE_IT(&DCMI_Handler,DCMI_IT_FRAME); //使能帧中断
|
||
__HAL_DCMI_ENABLE(&DCMI_Handler); //使能DCMI
|
||
}
|
||
|
||
//DCMI底层驱动,引脚配置,时钟使能,中断配置
|
||
//此函数会被HAL_DCMI_Init()调用
|
||
//hdcmi:DCMI句柄
|
||
void HAL_DCMI_MspInit(DCMI_HandleTypeDef* hdcmi)
|
||
{
|
||
GPIO_InitTypeDef GPIO_Initure;
|
||
|
||
__HAL_RCC_DCMI_CLK_ENABLE(); //使能DCMI时钟
|
||
|
||
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
|
||
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
|
||
__HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOC时钟
|
||
__HAL_RCC_GPIOD_CLK_ENABLE(); //使能GPIOD时钟
|
||
|
||
//初始化PA4
|
||
GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_6;
|
||
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用
|
||
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
|
||
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
|
||
GPIO_Initure.Alternate=GPIO_AF13_DCMI; //复用为DCMI
|
||
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化
|
||
|
||
//PB7,8,9
|
||
GPIO_Initure.Pin=GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
|
||
HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化
|
||
|
||
//PC6,7,8,9,11
|
||
GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|\
|
||
GPIO_PIN_9|GPIO_PIN_11;
|
||
HAL_GPIO_Init(GPIOC,&GPIO_Initure); //初始化
|
||
|
||
//PD3
|
||
GPIO_Initure.Pin=GPIO_PIN_3;
|
||
HAL_GPIO_Init(GPIOD,&GPIO_Initure); //初始化
|
||
|
||
HAL_NVIC_SetPriority(DCMI_IRQn,2,2); //抢占优先级1,子优先级2
|
||
HAL_NVIC_EnableIRQ(DCMI_IRQn); //使能DCMI中断
|
||
}
|
||
|
||
//DCMI DMA配置
|
||
//mem0addr:存储器地址0 将要存储摄像头数据的内存地址(也可以是外设地址)
|
||
//mem1addr:存储器地址1 当只使用mem0addr的时候,该值必须为0
|
||
//memblen:存储器位宽,可以为:DMA_MDATAALIGN_BYTE/DMA_MDATAALIGN_HALFWORD/DMA_MDATAALIGN_WORD
|
||
//meminc:存储器增长方式,可以为:DMA_MINC_ENABLE/DMA_MINC_DISABLE
|
||
void DCMI_DMA_Init(u32 mem0addr,u32 mem1addr,u16 memsize,u32 memblen,u32 meminc)
|
||
{
|
||
__HAL_RCC_DMA1_CLK_ENABLE(); //使能DMA1时钟
|
||
__HAL_LINKDMA(&DCMI_Handler,DMA_Handle,DMADMCI_Handler); //将DMA与DCMI联系起来
|
||
__HAL_DMA_DISABLE_IT(&DMADMCI_Handler,DMA_IT_TC); //先关闭DMA传输完成中断(否则在使用MCU屏的时候会出现花屏的情况)
|
||
|
||
DMADMCI_Handler.Instance=DMA1_Stream1; //DMA1数据流1
|
||
DMADMCI_Handler.Init.Request=DMA_REQUEST_DCMI; //DCMI的DMA请求
|
||
DMADMCI_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY; //外设到存储器
|
||
DMADMCI_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
|
||
DMADMCI_Handler.Init.MemInc=meminc; //存储器增量模式
|
||
DMADMCI_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD; //外设数据长度:32位
|
||
DMADMCI_Handler.Init.MemDataAlignment=memblen; //存储器数据长度:8/16/32位
|
||
DMADMCI_Handler.Init.Mode=DMA_CIRCULAR; //使用循环模式
|
||
DMADMCI_Handler.Init.Priority=DMA_PRIORITY_HIGH; //高优先级
|
||
DMADMCI_Handler.Init.FIFOMode=DMA_FIFOMODE_ENABLE; //使能FIFO
|
||
DMADMCI_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_HALFFULL; //使用1/2的FIFO
|
||
DMADMCI_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //存储器突发传输
|
||
DMADMCI_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设突发单次传输
|
||
HAL_DMA_DeInit(&DMADMCI_Handler); //先清除以前的设置
|
||
HAL_DMA_Init(&DMADMCI_Handler); //初始化DMA
|
||
|
||
//在开启DMA之前先使用__HAL_UNLOCK()解锁一次DMA,因为HAL_DMA_Statrt()HAL_DMAEx_MultiBufferStart()
|
||
//这两个函数一开始要先使用__HAL_LOCK()锁定DMA,而函数__HAL_LOCK()会判断当前的DMA状态是否为锁定状态,如果是
|
||
//锁定状态的话就直接返回HAL_BUSY,这样会导致函数HAL_DMA_Statrt()和HAL_DMAEx_MultiBufferStart()后续的DMA配置
|
||
//程序直接被跳过!DMA也就不能正常工作,为了避免这种现象,所以在启动DMA之前先调用__HAL_UNLOC()先解锁一次DMA。
|
||
__HAL_UNLOCK(&DMADMCI_Handler);
|
||
if(mem1addr==0) //开启DMA,不使用双缓冲
|
||
{
|
||
HAL_DMA_Start(&DMADMCI_Handler,(u32)&DCMI->DR,mem0addr,memsize);
|
||
}
|
||
else //使用双缓冲
|
||
{
|
||
HAL_DMAEx_MultiBufferStart(&DMADMCI_Handler,(u32)&DCMI->DR,mem0addr,mem1addr,memsize);//开启双缓冲
|
||
__HAL_DMA_ENABLE_IT(&DMADMCI_Handler,DMA_IT_TC); //开启传输完成中断
|
||
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn,2,3); //DMA中断优先级
|
||
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
|
||
}
|
||
}
|
||
|
||
//DCMI,启动传输
|
||
void DCMI_Start(void)
|
||
{
|
||
LCD_SetCursor(0,0);
|
||
LCD_WriteRAM_Prepare(); //开始写入GRAM
|
||
__HAL_DMA_ENABLE(&DMADMCI_Handler); //使能DMA
|
||
DCMI->CR|=DCMI_CR_CAPTURE; //DCMI捕获使能
|
||
}
|
||
|
||
//DCMI,关闭传输
|
||
void DCMI_Stop(void)
|
||
{
|
||
DCMI->CR&=~(DCMI_CR_CAPTURE); //关闭捕获
|
||
while(DCMI->CR&0X01); //等待传输完成
|
||
__HAL_DMA_DISABLE(&DMADMCI_Handler);//关闭DMA
|
||
}
|
||
|
||
//DCMI中断服务函数
|
||
void DCMI_IRQHandler(void)
|
||
{
|
||
HAL_DCMI_IRQHandler(&DCMI_Handler);
|
||
}
|
||
|
||
//捕获到一帧图像处理函数
|
||
//hdcmi:DCMI句柄
|
||
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
|
||
{
|
||
jpeg_data_process();//jpeg数据处理
|
||
LED1_Toggle;
|
||
ov_frame++;
|
||
//重新使能帧中断,因为HAL_DCMI_IRQHandler()函数会关闭帧中断
|
||
__HAL_DCMI_ENABLE_IT(&DCMI_Handler,DCMI_IT_FRAME);
|
||
}
|
||
|
||
void (*dcmi_rx_callback)(void);//DCMI DMA接收回调函数
|
||
//DMA1数据流1中断服务函数
|
||
void DMA1_Stream1_IRQHandler(void)
|
||
{
|
||
if(__HAL_DMA_GET_FLAG(&DMADMCI_Handler,DMA_FLAG_TCIF1_5)!=RESET)//DMA传输完成
|
||
{
|
||
__HAL_DMA_CLEAR_FLAG(&DMADMCI_Handler,DMA_FLAG_TCIF1_5);//清除DMA传输完成中断标志位
|
||
dcmi_rx_callback(); //执行摄像头接收回调函数,读取数据等操作在这里面处理
|
||
SCB_CleanInvalidateDCache(); //清除无效的D-Cache
|
||
}
|
||
}
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
//以下两个函数,供usmart调用,用于调试代码
|
||
|
||
//DCMI设置显示窗口
|
||
//sx,sy;LCD的起始坐标
|
||
//width,height:LCD显示范围.
|
||
void DCMI_Set_Window(u16 sx,u16 sy,u16 width,u16 height)
|
||
{
|
||
DCMI_Stop();
|
||
LCD_Clear(WHITE);
|
||
LCD_Set_Window(sx,sy,width,height);
|
||
OV5640_OutSize_Set(0,0,width,height);
|
||
LCD_SetCursor(0,0);
|
||
LCD_WriteRAM_Prepare(); //开始写入GRAM
|
||
__HAL_DMA_ENABLE(&DMADMCI_Handler); //开启DMA1,Stream1
|
||
DCMI->CR|=DCMI_CR_CAPTURE; //DCMI捕获使能
|
||
}
|
||
|
||
//通过usmart调试,辅助测试用.
|
||
//pclk/hsync/vsync:三个信号的有限电平设置
|
||
void DCMI_CR_Set(u8 pclk,u8 hsync,u8 vsync)
|
||
{
|
||
HAL_DCMI_DeInit(&DCMI_Handler);//清除原来的设置
|
||
DCMI_Handler.Instance=DCMI;
|
||
DCMI_Handler.Init.SynchroMode=DCMI_SYNCHRO_HARDWARE;//硬件同步HSYNC,VSYNC
|
||
DCMI_Handler.Init.PCKPolarity=pclk<<5; //PCLK 上升沿有效
|
||
DCMI_Handler.Init.VSPolarity=vsync<<7; //VSYNC 低电平有效
|
||
DCMI_Handler.Init.HSPolarity=hsync<<6; //HSYNC 低电平有效
|
||
DCMI_Handler.Init.CaptureRate=DCMI_CR_ALL_FRAME; //全帧捕获
|
||
DCMI_Handler.Init.ExtendedDataMode=DCMI_EXTEND_DATA_8B;//8位数据格式
|
||
HAL_DCMI_Init(&DCMI_Handler); //初始化DCMI
|
||
DCMI_Handler.Instance->CR|=DCMI_MODE_CONTINUOUS; //持续模式
|
||
}
|
||
|
||
|