IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32 IO口直接采集无FIFO摄像头方法总结 -> 正文阅读

[嵌入式]STM32 IO口直接采集无FIFO摄像头方法总结

1.概述
采用无数字口的单片机采集摄像头数据,通过模拟摄像头的采集时序进行图像采集,采集速度完全取决于单片机IO口的翻转速度和CPU处理速度;
硬件:stm32f401/411/405,M4内核,带DSP/FPU单精度浮点运算单元,当然F103也能用,但不建议用F103,因为RAM资源和内核,主频等等都跟不上,性能太差没有实际意义;
摄像头以OV7670无FIFO为例,最大像素640x480;
2.采集原理
(1)摄像头时序:
PCLK :像素时钟,一个像素时钟出一个8bit数据
XCLK:摄像头外部时钟,用于给摄像头提供时钟信号;
HSYNC:行同步信号,代表一行数据的起始,触发这个信号就开始一行图像的采集;
VSYNC:场同步信号,代表一场数据的起始,触发这个信号就开始一帧图像采集;
DATA 7-0:数据位bit7-bit0,一个RGB565的数据由两个data像素组成,从高到低依次传输
SCL&SDA:摄像头控制接口,寄存器配置,符合I2C/SCCB时序
RST :摄像头硬件复位信号,默认拉高
PWDN:上电时序相关,默认拉低
摄像头时钟24MHz 30帧/s,12MHz,15帧/s,可通过设置时钟分频和内部倍频机制合理控制时钟;
在这里插入图片描述
在这里插入图片描述

(2)IO配置
两种采集方式:
第一种采用全部普通GPIO模式,通过GPIO模拟摄像头时序,然后手动读取D0-D7的数据,尽量全部采集寄存器处理和指针处理方式会更快;
第二种采用定时器输入捕获+中断处理+DMA传输方式采集,控制行场信号,然后使用DMA搬运每行的数据;
(3)摄像头采集流程
第一种:GPIO直接采集

int liencnt=0;
int pixcnt=0;
unsigned char* pimage=NULL;
pimage=mymalloc(SRAMIN,cam_length);
mymemset(pimage,0,cam_length);//根据实际情况分配buffer大小,如果超过RAM,只能分配一行的大小,每采集一行就送显
while(1)
{
	unsigned char* pimg_temp=pimage;
	while(OV_VSYNC==1)//场信号为高时阻塞
	while(OV_VSYNC==0)//场信号为低开始采集
	{
		for (linecnt=0;linecnt<cam_height;linecnt++)
		{
			while(OV_HSYNC==0);  //行信号为高时采集有效数据,为低时是无效数据,阻塞处理
			// while(OV_HSYNC==1);
			for(pixcnt=0;pixcnt<cam_width*2;pixcnt++)
			{
				while(OV_PCLK==0);                     //在PCLK下降沿读取数据
				*pimg_temp=GPIOC->IDR&0XFF; //每次取8位数据
				while(OV_PCLK==1);
				pimg_temp++;
			}			
			/*
			for(pixcnt=0;pixcnt<cam_width*2;pixcnt++)
			{
				//UART_SEND(pimage,cam_length);  //一行一行送显
			}
			*/
		}	
	}
}

第二种:中断+DMA采集

场中断:低电平有效
行中断:定时器输入捕获

static void VSYNC_Exit_Init(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  			
        GPIO_Init(GPIOB, &GPIO_InitStructure);     
        
	EXTI_InitTypeDef EXTI_InitStructure;
	//初始化中断脚复用时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	//配置中断源
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource7);
	
	EXTI_ClearITPendingBit(EXTI_Line7);
	EXTI_InitStructure.EXTI_Line = EXTI_Line7;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rasing;  //上升沿代表一帧采集结束,低电平为有效像素
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_InitTypeDef NVIC_InitStructure ;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;		//通道设置为外部中断线
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; 	//中断抢占先等级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;           	//中断响应优先级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           	//打开中断
        NVIC_Init(&NVIC_InitStructure);                             
}
void TIM4_Cap_Init(u16 arr,u16 psc)
{	 

	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;	
        NVIC_InitTypeDef NVIC_InitStructure;
   	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);	//使能TIM4时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOB,GPIO_Pin_6);		
	
	//初始化定时器4	 
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  
	//初始化TIM4输入捕获参数
	TIM4_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
	TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
	TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
	TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
	TIM4_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
	TIM_ICInit(TIM4, &TIM4_ICInitStructure);

//中断分组初始化

	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 	
	TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断		
	TIM_Cmd(TIM4,ENABLE ); 	//使能定时器5
	
}
	
	void 	TIM4_DMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpaddr,u32 cmaddr,u32 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//打开DMA1时钟	

        DMA_DeInit(DMA_CHx);   
        DMA_InitStructure.DMA_Channel=DMA_Channel_2;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpaddr;//外设地址 
	DMA_InitStructure.DMA_MemoryBaseAddr = cmaddr;  //内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //传输方向外设到内存
	DMA_InitStructure.DMA_BufferSize = cndtr;  //传输数量
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不自增  
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址自增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据大小为半字节
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据大小为半字节
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
	DMA_Init(DMA_CHx, &DMA_InitStructure);  
  	
} 
void capture_start(void)
{
	EXIT_ITConfig(EXIT_Line7,ENABLE);//开中断
	TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断
	TIM_DMACmd(TIM4, TIM_DMA_CC1, ENABLE);
}
void capture_stop(void)
{	
 	EXIT_ITConfig(EXIT_Line7,DISABLE);//关中断
 	TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,DISABLE);//关中断,不捕获
 	TIM_DMACmd(TIM4, TIM_DMA_CC1,DISABLE);
}



//PCLK---A4
//XCLK---A8
//VSYNC---B7
//HREF---B6 
//D0-D7---C0-C7
//SCL---B8
//SDA---B9
//PWND---B4
//RST---B5

int linecnt=0;
void EXIT9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXIT_Line7)==SET)  //一帧图像采集完
	{
		EXIT_ClearITPendingBit(EXIT_Line7);
		capture_stop();
		read_ok=1;//采集完成标志
		frame++;
		linecnt=0;		
	}
}
//TIM4_CH1  DMA请求DMA1_Stream_0  channel_2
//捕获上升沿
void TIM4_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM4,TIM_IT_CC1)!=RESET)		//发生一次捕获事件,开始DAM传输
	{
		TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
		TIM_SetCounter(TIM4,0);//重新计数;
		if(linecnt <=cam_height)   
		{
			//这里没有采用DMA传输完成中断,如果采用中断处理,可以在回调函数中处理图像格式,F4支持双DMA缓存
			while(DMA_GetFlagStatus(DMA1_FLAG_TC2)==RESET);//等待DMA1传输完成
			DMA_ClearFlag(DMA1_FLAG_TC2);//清除标志
			//DMA采集每改变一次地址是需要将DMA失能后才能改变      
			DMA1_Stream0->CR &= (uint32_t)~DMA_SxCR_EN;//失能
			DMA1_Stream0->M0AR = (uint32_t)&(Image[linecnt++]);  //新的一行地址 
			DMA1_Stream0->CR |= DMA_SxCR_EN;  //使能
		}    
		if(linecnt >cam_height)    
		{  
			DMA1_Stream0->CR &= (uint32_t)~DMA_SxCR_EN; 
			DMA_Cmd(DMA1_Stream0, DISABLE);
			TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,DISABLE);//关中断,不捕获
		}
	}
}
		```
		
	
	
	
	
	
	
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-01-14 02:08:40  更:2022-01-14 02:11:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 1:31:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码