移植摄像头视频传输JPEG格式代码学习过程

目录

理解厂家的源代码

ov2640.c文件

DCMI.c文件

SCCB.c文件

main.c文件

总结


理解厂家的源代码

这里我使用的是普中的ov2640模块

ov2640.c文件

返回值:0,成功。//配置完以后,默认输出是 1600*1200 尺寸的图片!!

初始化ov2640通信协议sccb(类似i2c协议)的io口,进行通讯。

初始化摄像头(复位,软复位,分辨率)

通过sccb(i2c)查询厂家id,来判断是否连接上ov2640,错误则返回1/2 。

26. SCCB_Init(); //初始化 SCCB 的 IO 口
27. SCCB_WR_Reg(OV2640_DSP_RA_DLMT, 0x01); //操作 sensor 寄存器
28. SCCB_WR_Reg(OV2640_SENSOR_COM7, 0x80); //软复位 OV2640
29. delay_ms(50);
30. reg=SCCB_RD_Reg(OV2640_SENSOR_MIDH); //读取厂家 ID 高八位
31. reg<<=8;
32. reg|=SCCB_RD_Reg(OV2640_SENSOR_MIDL); //读取厂家 ID 低八位
33. if(reg!=OV2640_MID)
34. {
35. printf("MID:%d\r\n",reg);
36. return 1;
37. }
38. reg=SCCB_RD_Reg(OV2640_SENSOR_PIDH); //读取厂家 ID 高八位
39. reg<<=8;
40. reg|=SCCB_RD_Reg(OV2640_SENSOR_PIDL); //读取厂家 ID 低八位
41. if(reg!=OV2640_PID)
42. {
43. printf("HID:%d\r\n",reg);
44. return 2;
45. }
46. //初始化 OV2640,采用 SXGA 分辨率(1600*1200)
47. for(i=0;i<sizeof(ov2640_sxga_init_reg_tbl)/2;i++)
48. {
49. SCCB_WR_Reg(ov2640_sxga_init_reg_tbl[i][0],ov2640_sxga_init_reg_tbl[i][1]);
50. }
51. return 0x00; //ok
52

值得一提的是ov2640.c中的这四个数据配置函数,会经常用到。

OV2640_Window_Set 函数, 该函数用于设置传感器输出窗口;
OV2640_ImageSize_Set 函数, 用于设置图像尺寸;
OV2640_ImageWin_Set 函数, 用于设置图像窗口大小;
OV2640_OutSize_Set 函数, 用于设置图像输出大小;

OV2640 UXGA 初始化寄存器序列表

1. //OV2640 UXGA 初始化寄存器序列表
2. //此模式下帧率为 15 帧
3. //UXGA(1600*1200)
4. const u8 ov2640_uxga_init_reg_tbl[][2]=
5. {
6. 0xff, 0x00,
7. ……//省略部分代码
8. 0x05, 0x00,
9. };
10. //OV2640 SVGA 初始化寄存器序列表
11. //此模式下,帧率可以达到 30 帧
12. //SVGA 800*600
13. const u8 ov2640_svga_init_reg_tbl[][2]=
14. {
15. 0xff, 0x00,
16. ……//省略部分代码17. 0x05, 0x00,
18. };
19. const u8 ov2640_yuv422_reg_tbl[][2]=
20. {
21. 0xFF, 0x00,
22. ……//省略部分代码
23. 0x00, 0x00,
24. };
25. const u8 ov2640_jpeg_reg_tbl[][2]=
26. {
27. 0xff, 0x01,
28. ……//省略部分代码
29. 0xe0, 0x00,
30. };
31. const u8 ov2640_rgb565_reg_tbl[][2]=
32. {
33. 0xFF, 0x00,
34. ……//省略部分代码
35. 0xe0, 0x00,
36. };
以上代码, 我们省略了很多(全部贴出来太长了) , 里面总共有 5 个数组。

我们大概了解下数组结构, 每个数组条目的第一个字节为寄存器号(也就是寄存
器地址) , 第二个字节为要设置的值, 比如{0xff, 0x01}, 就表示在 0Xff 地址,
写入 0X01 这个值。
 

DCMI.c文件

1.介绍dcmi功能

介绍dcmi功能,在翻看手册后发现dcmi在f4中是包装好的串口组,并且与dma的功能挂钩,在dcmi串口组接收到数据并存入寄存器中时通过dma将外设中的数据发送进内存中缓存,所以在使用官方帮我们包装好的dcmi功能库,能大大减小我们开发者的开发难度。

摄像头接口可以捕获 8 位、 10 位、 12 位或14 位数据。如果使用的位数少于 14,则必须将未使用的输入引脚接地。

数据与 PIXCLK 保持同步,并根据像素时钟的极性在像素时钟上升沿/下降沿发生变化。
HSYNC 信号指示行的开始/结束。
VSYNC 信号指示帧的开始/结束。

对于压缩数据, DCMI 仅支持硬件同步模式。这种情况下, VSYNC 指示图像的开始/结束,
HSYNC 则用作“数据有效”信号。

当 DCMI_CR 寄存器中的 CAPTURE 位置 1 时,激活 DMA 接口。摄像头接口每次在其寄存
器中收到一个完整的 32 位数据块时,都将触发一个 DMA 请求。

JPEG 格式
要允许接收 JPEG 图像,必须将 DCMI_CR 寄存器中的 JPEG 位置 1。 JPEG 图像不按行和
帧存储,因此 VSYNC 信号用于启动捕获过程,而 HSYNC 则用作数据使能信号。行中包含
的字节数可能不是 4 的倍数,因此处理此类情况时应十分谨慎,因为需要每次从捕获的数据
形成一个完整的 32 位字时,才生成一个 DMA 请求。检测到帧结束并且尚未凑成 32 位字
时,将使用“0”进行填充,并触发一个 DMA 请求。
裁剪功能和内嵌码同步不适用于 JPEG 格式。

硬件同步模式
硬件同步模式下将使用两个同步信号 (HSYNC/VSYNC)。
根据摄像头模块/模式的不同,可能在水平/垂直同步期间内发送数据。由于系统会忽略
HSYNC/VSYNC 信号有效电平期间内接收的所有数据, HSYNC/VSYNC 信号相当于消
隐信号。
为了正确地将图像传输到 DMA/RAM 缓冲区,数据传输将与 VSYNC 信号同步。选择硬件同
步模式并启用捕获( DCMI_CR 中的 CAPTURE 位置 1)时,数据传输将与 VSYNC 信号的
无效电平同步(开始下一帧时)。
之后传输便可以连续执行,由 DMA 将连续帧传输到多个连续的缓冲区或一个具有循环特性的
缓冲区。为了允许 DMA 管理连续帧,每一帧结束时都将激活 VSIF(垂直同步中断标志)。
 

2.解析dcmi代码

初始化dcmi串口

56. //DCMI 初始化
57. void My_DCMI_Init(void)
58. {
59. GPIO_InitTypeDef GPIO_InitStructure;
60. NVIC_InitTypeDef NVIC_InitStructure;
61.
62.
63. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|R
CC_AHB1Periph_GPIOE, ENABLE);//使能 GPIOA B C E 时钟
64. RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);//使能 DCMI 时钟
65. //PA4/6 初始化设置
66. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//PA4/6 复用功能输出
67. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能输出
68. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
69. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
70. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
71. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
72.
73. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;// PB6/7 复用功能输出
74. GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
75.
76. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;/
/PC6/7/8/9/11 复用功能输出
77. GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
78.
79. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//PE5/6 复用功能输出
80. GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
81.
82. GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13 DCMI_HSYNC
83. GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13 DCMI_PCLK84. GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13 DCMI_VSYNC
85. GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13 DCMI_D0
86. GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13 DCMI_D1
87. GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13 DCMI_D2
88. GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13 DCMI_D3
89. GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
90. GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13 DCMI_D5
91. GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13 DCMI_D6
92. GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13 DCMI_D7
93.
94. DCMI_DeInit();//清除原来的设置
95.
96. DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;//连续模式
97. DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;//全帧捕获
98. DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;//8 位数据格式
99. DCMI_InitStructure.DCMI_HSPolarity= DCMI_HSPolarity_Low;//HSYNC 低电平有效
100. DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Rising;//PCLK 上升沿有效
101. DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;//硬件同步 HSYNC,VSYNC
102. DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_Low;//VSYNC 低电平有效
103. DCMI_Init(&DCMI_InitStructure);
104.
105. DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断
106.
107. DCMI_Cmd(ENABLE); //DCMI 使能
108.
109. NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
110. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级 1
111. NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; //子优先级 3
112. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
113. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 VIC 寄存器、
114.
115. }

分别进行了io口初始化和DCMI结构体初始化,以及DCMI的中断初始化。

外包一个dcmi打开和关闭代码


117. void DCMI_Start(void)
118. {
119. //对于一些型号彩屏, 需要修改下窗口, 要么-1, 要么不减 1
120. LCD_Set_Window(0, 0, tftlcd_data.width-1, tftlcd_data.height-1); //设置点的位置
121. DMA_Cmd(DMA2_Stream1, ENABLE);//开启 DMA2,Stream1
122. DCMI_CaptureCmd(ENABLE);//DCMI 捕获使能
123. }
124. //DCMI,关闭传输
125. void DCMI_Stop(void)
126. {
127. DCMI_CaptureCmd(DISABLE);//DCMI 捕获使关闭128.
129. while(DCMI->CR&0X01); //等待传输结束
130.
131. DMA_Cmd(DMA2_Stream1,DISABLE);//关闭 DMA2,Stream1
132. }

DCMI中断服务函数 


13. void DCMI_IRQHandler(void)
14. {
15. if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)//捕获到一帧图像
16. {
17. jpeg_data_process(); //jpeg 数据处理
18. DCMI_ClearITPendingBit(DCMI_IT_FRAME);//清除帧中断
19. LED2=!LED2;
20. ov_frame++;
21. }
22. }

DMA初始化函数

23. //DCMI DMA 配置
24. //DMA_Memory0BaseAddr:存储器地址 将要存储摄像头数据的内存地址(也可以是外设地址)
25. //DMA_BufferSize:存储器长度 0~65535
26. //DMA_MemoryDataSize:存储器位宽
27. //DMA_MemoryDataSize: 存 储 器 位
宽 @defgroup DMA_memory_data_size :DMA_MemoryDataSize_Byte/DMA_MemoryDataSize_HalfWord/DMA_
MemoryDataSize_Word
28. //DMA_MemoryInc: 存 储 器 增 长 方
式 @defgroup DMA_memory_incremented_mode /** @defgroup DMA_memory_incremented_mode : DMA_Mem
oryInc_Enable/DMA_MemoryInc_Disable
29. void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DM
A_MemoryInc)
30. {
31. DMA_InitTypeDef DMA_InitStructure;
32.
33. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
34. DMA_DeInit(DMA2_Stream1);
35. while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待 DMA2_Stream1 可配置
36.
37. /* 配置 DMA Stream */
38. DMA_InitStructure.DMA_Channel = DMA_Channel_1; //通道 1 DCMI 通道
39. DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR
40. DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//DMA 存储器 0 地址
41. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式
42. DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;//数据传输量43. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
44. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;//存储器增量模式
45. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据长度:32
位
46. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;//存储器数据长度
47. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式
48. DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
49. DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO 模式
50. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全 FIFO
51. DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
52. DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
53. DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化 DMA Stream
54.
55. }

在这里例程文件是将其转存入fifo,在后面需要更改到srom或者内存中

SCCB.c文件

普中的例程中是用软件模拟I2C

void SCCB_Init(void)
{				
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟
	//GPIOF9,F10初始化设置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;//PD6,7 推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  //PD6,7 推挽输出
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
 
	GPIO_SetBits(GPIOD,GPIO_Pin_6|GPIO_Pin_7);
	SCCB_SDA_OUT();	   
}			 

//SCCB起始信号
//当时钟为高的时候,数据线的高到低,为SCCB起始信号
//在激活状态下,SDA和SCL均为低电平
void SCCB_Start(void)
{
    SCCB_SDA=1;     //数据线高电平	   
    SCCB_SCL=1;	    //在时钟线高的时候数据线由高至低
    delay_us(50);  
    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=0;	    //数据线恢复低电平,单操作函数必要	  
}

//SCCB停止信号
//当时钟为高的时候,数据线的低到高,为SCCB停止信号
//空闲状况下,SDA,SCL均为高电平
void SCCB_Stop(void)
{
    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=1;	
    delay_us(50); 
    SCCB_SDA=1;	
    delay_us(50);
}  
//产生NA信号
void SCCB_No_Ack(void)
{
	delay_us(50);
	SCCB_SDA=1;	
	SCCB_SCL=1;	
	delay_us(50);
	SCCB_SCL=0;	
	delay_us(50);
	SCCB_SDA=0;	
	delay_us(50);
}
//SCCB,写入一个字节
//返回值:0,成功;1,失败. 
u8 SCCB_WR_Byte(u8 dat)
{
	u8 j,res;	 
	for(j=0;j<8;j++) //循环8次发送数据
	{
		if(dat&0x80)SCCB_SDA=1;	
		else SCCB_SDA=0;
		dat<<=1;
		delay_us(50);
		SCCB_SCL=1;	
		delay_us(50);
		SCCB_SCL=0;		   
	}			 
	SCCB_SDA_IN();		//设置SDA为输入 
	delay_us(50);
	SCCB_SCL=1;			//接收第九位,以判断是否发送成功
	delay_us(50);
	if(SCCB_READ_SDA)res=1;  //SDA=1发送失败,返回1
	else res=0;         //SDA=0发送成功,返回0
	SCCB_SCL=0;		 
	SCCB_SDA_OUT();		//设置SDA为输出    
	return res;  
}	 
//SCCB 读取一个字节
//在SCL的上升沿,数据锁存
//返回值:读到的数据
u8 SCCB_RD_Byte(void)
{
	u8 temp=0,j;    
	SCCB_SDA_IN();		//设置SDA为输入  
	for(j=8;j>0;j--) 	//循环8次接收数据
	{		     	  
		delay_us(50);
		SCCB_SCL=1;
		temp=temp<<1;
		if(SCCB_READ_SDA)temp++;   
		delay_us(50);
		SCCB_SCL=0;
	}	
	SCCB_SDA_OUT();		//设置SDA为输出    
	return temp;
} 							    
//写寄存器
//返回值:0,成功;1,失败.
u8 SCCB_WR_Reg(u8 reg,u8 data)
{
	u8 res=0;
	SCCB_Start(); 					//启动SCCB传输
	if(SCCB_WR_Byte(SCCB_ID))res=1;	//写器件ID	  
	delay_us(100);
  	if(SCCB_WR_Byte(reg))res=1;		//写寄存器地址	  
	delay_us(100);
  	if(SCCB_WR_Byte(data))res=1; 	//写数据	 
  	SCCB_Stop();	  
  	return	res;
}		  					    
//读寄存器
//返回值:读到的寄存器值
u8 SCCB_RD_Reg(u8 reg)
{
	u8 val=0;
	SCCB_Start(); 				//启动SCCB传输
	SCCB_WR_Byte(SCCB_ID);		//写器件ID	  
	delay_us(100);	 
  	SCCB_WR_Byte(reg);			//写寄存器地址	  
	delay_us(100);	  
	SCCB_Stop();   
	delay_us(100);	   
	//设置寄存器地址后,才是读
	SCCB_Start();
	SCCB_WR_Byte(SCCB_ID|0X01);	//发送读命令	  
	delay_us(100);
  	val=SCCB_RD_Byte();		 	//读取数据
  	SCCB_No_Ack();
  	SCCB_Stop();
  	return val;
}

因为stm32本来就带有硬件I2C模块,借鉴以前使用过I2C的硬件代码可参考下面这个博主的文章。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010168781/article/details/126257339

main.c文件

这里定义了一个非常大的数组 jpeg_buf(124KB) , 用来存储 JPEG 数据,
因为 1600*1200 大小的 jpeg 图片, 有可能大于 120K, 所以必须将这个数组尽
量设置大一点。
在 main.c 里面, 总共有 4 个函数: jpeg_data_process、 jpeg_test、
rgb565_test 和 main 函数。 其中, jpeg_data_process 函数用于处理 JPEG 数
据的接收, 在 DCMI_IRQHandler 函数里面被调用, 通过一个 jpeg_data_ok 变
量与 jpeg_test 函数共同控制 JPEG 的数据传送。 jpeg_test 函数将 OV2640
设置为 JPEG 模式, 该函数接收 OV2640 的 JPEG 数据, 并通过串口 1 发送给
上位机。 rgb565_test 函数将 OV2640 设置为 RGB565 模式, 并将接收到的数据,
直接传送给 LCD, 处理过程完全由硬件实现, CPU 完全不用理会。 最后, main 函
数就相对简单了, 大家看代码即可。

jpeg_data_process()


38. //处理 JPEG 数据
39. //当采集完一帧 JPEG 数据后,调用此函数,切换 JPEG BUF.开始下一帧采集.
40. void jpeg_data_process(void)
41. {
42. if(ov2640_mode)//只有在 JPEG 格式下,才需要做处理.
43. {
44. if(jpeg_data_ok==0) //jpeg 数据还未采集完?
45. {
46. DMA_Cmd(DMA2_Stream1, DISABLE);//停止当前传输
47. while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待 DMA2_Stream1 可配置
48. jpeg_data_len=jpeg_buf_size-DMA_GetCurrDataCounter(DMA2_Stream1);//得到此次数据传
输的长度
49.
50. jpeg_data_ok=1; //标记 JPEG 数据采集完按成,等待其他函数处理
51. }
52. if(jpeg_data_ok==2) //上一次的 jpeg 数据已经被处理了
53. {
54. DMA2_Stream1->NDTR=jpeg_buf_size;
55. DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_buf_size);//传输长度为 jpeg_buf_size*4 字
节
56. DMA_Cmd(DMA2_Stream1, ENABLE); //重新传输
57. jpeg_data_ok=0; //标记数据未采集
58. }
59. }
60. }

判断摄像头传输出来的数据是否采集完成,如果没采集完成标志位jpeg_data_ok为0,将关闭DMA传输等待采集完成,将标志位jpeg_data_ok置1,开始处理数据,如果处理完成,标志位jpeg_data_ok置2。检测到标志位为2,将DMA打开传输下一帧数据。

jpeg_test()

61. //JPEG 测试
62. //JPEG 数据,通过串口 1 发送给电脑.
63. void jpeg_test(void)
64. {
65. u32 i;
66. u8 *p;
67. u8 key;
68. u8 effect=0,saturation=2,contrast=2;
69. u8 size=3; //默认是 QVGA 320*240 尺寸70. u8 msgbuf[15]; //消息缓存区
71. LCD_Clear(WHITE);
72. FRONT_COLOR=RED;
73. LCD_ShowString(30,50,200,16,16,"PRECHIN-STM32");
74. LCD_ShowString(30,70,200,16,16,"OV2640 JPEG Mode");
75. LCD_ShowString(30,100,200,16,16,"KEY0:Contrast"); //对比度
76. LCD_ShowString(30,120,200,16,16,"KEY1:Saturation"); //色彩饱和度
77. LCD_ShowString(30,140,200,16,16,"KEY2:Effects"); //特效
78. LCD_ShowString(30,160,200,16,16,"KEY_UP:Size"); //分辨率设置
79. sprintf((char*)msgbuf,"JPEG Size:%s",JPEG_SIZE_TBL[size]);
80. LCD_ShowString(30,180,200,16,16,msgbuf); //显示当前 JPEG 分辨率
81.
82. OV2640_JPEG_Mode(); //JPEG 模式
83. My_DCMI_Init(); //DCMI 配置
84. DCMI_DMA_Init((u32)&jpeg_buf,jpeg_buf_size,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable)
;//DCMI DMA 配置
85. OV2640_OutSize_Set(jpeg_img_size_tbl[size][0],jpeg_img_size_tbl[size][1]);// 设 置 输 出 尺
寸
86. DCMI_Start(); //启动传输
87. while(1)
88. {
89. if(jpeg_data_ok==1) //已经采集完一帧图像了
90. {
91. p=(u8*)jpeg_buf;
92. LCD_ShowString(30,210,210,16,16,"Sending JPEG data..."); //提示正在传输数据
93. for(i=0;i<jpeg_data_len*4;i++) //dma 传输 1 次等于 4 字节,所以乘以 4.
94. {
95. while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); //循环发送,直到发
送完毕
96. USART_SendData(USART1,p[i]);
97. key=KEY_Scan(0);
98. if(key)break;
99. }
100. if(key) //有按键按下,需要处理
101. {
102. LCD_ShowString(30,210,210,16,16,"Quit Sending data ");//提示退出数据传输
103. switch(key)
104. {
105. case KEY0_PRESS: //对比度设置
106. contrast++;
107. if(contrast>4)contrast=0;
108. OV2640_Contrast(contrast);
109. sprintf((char*)msgbuf,"Contrast:%d",(signed char)contrast-2);
110. break;111. case KEY1_PRESS: //饱和度 Saturation
112. saturation++;
113. if(saturation>4)saturation=0;
114. OV2640_Color_Saturation(saturation);
115. sprintf((char*)msgbuf,"Saturation:%d",(signed char)saturation-2);
116. break;
117. case KEY2_PRESS: //特效设置
118. effect++;
119. if(effect>6)effect=0;
120. OV2640_Special_Effects(effect);//设置特效
121. sprintf((char*)msgbuf,"%s",EFFECTS_TBL[effect]);
122. break;
123. case KEY_UP_PRESS: //JPEG 输出尺寸设置
124. size++;
125. if(size>8)size=0;
126. OV2640_OutSize_Set(jpeg_img_size_tbl[size][0],jpeg_img_size_tbl[siz
e][1]);//设置输出尺寸
127. sprintf((char*)msgbuf,"JPEG Size:%s",JPEG_SIZE_TBL[size]);
128. break;
129. }
130. LCD_Fill(30,180,239,190+16,WHITE);
131. LCD_ShowString(30,180,210,16,16,msgbuf);//显示提示内容
132. delay_ms(800);
133. }else LCD_ShowString(30,210,210,16,16,"Send data complete!!");//提示传输结束设
置
134. jpeg_data_ok=2; //标记 jpeg 数据处理完了,可以让 DMA 去采集下一帧了.
135. }
136. }
137. }

前面部分先初始化了处理数据要用到的外设,dcmi,dma。将摄像头定义为jpeg模式,然后打开数据传输,然后用指针指向dma接收的地址,循环读取指针指向的地址并将它用usart1发送出去(写入发送数据寄存器一次后要循环判断有没有发送完,没发送完就在循环中等待ps:曾经试过写串口发送数据代码时没有判断是否发送成功就又写入下一个数据,导致寄存器覆盖太快数据掉包严重)在循环中还要判断是否有按键按下,如果有要跳出循环执行按键对应的功能。

按键按下后用switch case来判断该执行哪个程序,执行完成后将数据处理标志位置2,在jpeg_data_process()中开启执行数据传输程序。

main()

209. int main(void)
210. {
211. u8 key;
212. u8 t;
213.
214. SysTick_Init(168); //初始化延时函数
215. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
216. USART1_Init(921600); //初始化串口波特率为 921600
217. LED_Init(); //初始化 LED
218. TFTLCD_Init(); //LCD 初始化
219. KEY_Init(); //按键初始化
220. TIM3_Init(10000-1,8400-1);//10Khz 计数,1 秒钟中断一次
221.
222. FRONT_COLOR=RED;//设置字体为红色
223. LCD_ShowString(30,50,200,16,16,"PRECHIN-STM32");
224. LCD_ShowString(30,70,200,16,16,"OV2640 TEST");
225. LCD_ShowString(30,90,200,16,16,"www.prechin.net");
226. while(OV2640_Init())//初始化 OV2640
227. {
228. LCD_ShowString(30,130,240,16,16,"OV2640 ERR");
229. delay_ms(200);
230. LCD_Fill(30,130,239,170,WHITE);
231. delay_ms(200);
232. }
233. LCD_ShowString(30,130,200,16,16,"OV2640 OK");
234. while(1)
235. {
236. key=KEY_Scan(0);
237. if(key==KEY0_PRESS) //RGB565 模式238. {
239. ov2640_mode=0;
240. break;
241. }else if(key==KEY1_PRESS) //JPEG 模式
242. {
243. ov2640_mode=1;
244. break;
245. }
246. t++;
247. if(t==100)
248. {
249. LED1=!LED1;
250. LCD_ShowString(30,150,230,16,16,"KEY0:RGB565 KEY1:JPEG"); //闪烁显示提示信息
251. }
252. if(t==200)
253. {
254. LCD_Fill(30,150,210,150+16,WHITE);
255. t=0;
256. }
257. delay_ms(5);
258. }
259. if(ov2640_mode)jpeg_test();
260. else rgb565_test();
261. }

主要执行初始化以及初始化时lcd的显示步骤功能,在while(1)循环中执行识别摄像头输出模式是否改变。

总结

主要编程的模块有sccb(i2c),dma,dcmi,usart模块。

sccb进行对摄像头模块的写操作(改变摄像头模式等功能)在初始化时用到,而dcmi这个st包装好的总线,初始化后自动进行数据处理进dcmi的数据寄存器中,再又dma将数据寄存器中的视频数据搬到fifo中存储,然后在应用层,识别fifo中的这帧数据处理完成没有(用串口发送出去),处理完成则接收下一帧。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
移植Linux摄像头驱动需要以下步骤: 1.了解摄像头硬件的接口和协议,以及摄像头的型号和厂商信息。 2.下载并安装Linux内核源代码,确保内核版本与目标设备的内核版本相同。 3.编写摄像头驱动程序,包括初始化、打开、关闭、读取和写入等函数。 4.将驱动程序编译成内核模块或静态链接到内核中。 5.将编译好的驱动程序拷贝到目标设备上,并加载驱动程序。 6.测试驱动程序是否正常工作,包括摄像头是否能够被正确识别和使用。 以下是一个简单的示例,演示如何移植Linux摄像头驱动: 1.首先,需要了解摄像头硬件的接口和协议,以及摄像头的型号和厂商信息。 2.下载并安装Linux内核源代码,确保内核版本与目标设备的内核版本相同。 3.编写摄像头驱动程序,包括初始化、打开、关闭、读取和写入等函数。例如: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/usb.h> static struct usb_device_id my_usb_table[] = { { USB_DEVICE(0x1234, 0x5678) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, my_usb_table); static int my_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { printk(KERN_INFO "USB device (%04X:%04X) plugged\n", id->idVendor, id->idProduct); return 0; } static void my_usb_disconnect(struct usb_interface *interface) { printk(KERN_INFO "USB device removed\n"); } static struct usb_driver my_usb_driver = { .name = "my_usb_driver", .id_table = my_usb_table, .probe = my_usb_probe, .disconnect = my_usb_disconnect, }; static int __init my_usb_init(void) { return usb_register(&my_usb_driver);} static void __exit my_usb_exit(void) { usb_deregister(&my_usb_driver); } module_init(my_usb_init); module_exit(my_usb_exit); MODULE_LICENSE("GPL"); ``` 4.将驱动程序编译成内核模块或静态链接到内核中。例如: ```bash make -C /usr/src/linux M=$PWD modules ``` 5.将编译好的驱动程序拷贝到目标设备上,并加载驱动程序。例如: ```bash insmod my_usb_driver.ko ``` 6.测试驱动程序是否正常工作,包括摄像头是否能够被正确识别和使用。例如: ```bash dmesg | tail ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值