stm32f4中用SD卡存储DCMI的图像

出处:eefocus 发布于:2018-08-22 13:59:59

  因为自己本科做的创新性实验和飞思卡尔小车都是摄像头的,研究生也做的视频处理方向。后来,想做一个小视频监制,闲麻烦,没有用那TI的DM6446,就用的手头stm32f4开发板,由于没有LCD显示屏,我只能直接把DCMI图像保存在内部RAM中,再保存到SD里,在上位机读取SD卡转换成图片,我就用VC+OPENCV。

  现在说说做的流程吧。摄像头是买的OV9665 。直接接的是DCMI接口。而SD卡不能接SDIO了,因为我这开发板是100引脚封装的,SDIO和DCMI复用引脚冲突。之后SD卡选用的是SPI接口。

  1.关于SPI接口的SD卡读写操作,我在前几篇博客中写过,也附带了写好的FATFS文件系统程序,大家可以参考,我这里就不多写了。

  2.重要的是关于DCMI的摄像头接口,主要是在DCMI的配置上和DMA的配置,下面着重进行讲解。

  void OV9655_HW_Init(void)

  {

  GPIO_InitTypeDef GPIO_InitStructure;

  I2C_InitTypeDef  I2C_InitStruct;

  /*** Configures the DCMI GPIOs to interface with the OV9655 camera module ***/

  /* Enable DCMI GPIOs clocks */

  /* Enable DCMI GPIOs clocks */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC |

  RCC_AHB1Periph_GPIOE, ENABLE);

  /* Enable DCMI clock */

  RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);

  /* Connect DCMI pins to AF13 ************************************************/

  GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI);//HSYNC

  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI);//PIXCLK

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI);//DCMI_D5

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI);//VSYNC

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_DCMI);//DCMI_D0

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_DCMI);//DCMI_D1

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_DCMI);//DCMI_D2

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_DCMI);//DCMI_D3

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource4, GPIO_AF_DCMI);//DCMI_D4

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_DCMI);//DCMI_D6

  GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_DCMI);//DCMI_D7

  /* DCMI GPIO configuration **************************************************/

  /* HSYNC(PA4), PIXCLK(PA6) */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* DCMI_D5(PB6), VSYNC(PB7) */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;

  GPIO_Init(GPIOB, &GPIO_InitStructure);

  /* DCMI_D0(PC6), DCMI_D1(PC7) */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;

  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* DCMI_D2(PE0), DCMI_D3(PE1), DCMI_D4(PE4), DCMI_D6(PE5), DCMI_D7(PE6),*/

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;

  GPIO_Init(GPIOE, &GPIO_InitStructure);

  /****** Configures the I2C1 used for OV9655 camera module configuration *****/

  /* I2C1 clock enable */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  /* GPIOB clock enable */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

  /* Connect I2C1 pins to AF4 */

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1);

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);

  /* Configure I2C1 GPIOs */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_Init(GPIOB, &GPIO_InitStructure);

  /* Configure I2C1 */

  /* I2C DeInit */

  I2C_DeInit(I2C1);

  /* Enable the I2C peripheral */

  I2C_Cmd(I2C1, ENABLE);

  /* Set the I2C structure parameters */

  I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;

  I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;

  I2C_InitStruct.I2C_OwnAddress1 = 0xFE;

  I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;

  I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

  I2C_InitStruct.I2C_ClockSpeed = 30000;

  /* Initialize the I2C peripheral w/ selected parameters */

  I2C_Init(I2C1, &I2C_InitStruct);

  }

  关于DCMI和DMA的初始化程序如下

  void OV9655_Init(ImageFormat_TypeDef ImageFormat)

  {

  DCMI_InitTypeDef DCMI_InitStructure;

  DMA_InitTypeDef  DMA_InitStructure;

  /*** Configures the DCMI to interface with the OV9655 camera module ***/

  /* Enable DCMI clock */

  RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);

  /* DCMI configuration */

  DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_SnapShot;//DCMI_CaptureMode_Continuous;

  DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;

  DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Falling;

  DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High;

  DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High;

  DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_1of4_Frame;//DCMI_CaptureRate_All_Frame;

  DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;

  //----- mask interrupt for DCMI -----

  DCMI_ITConfig(DCMI_IT_VSYNC, ENABLE);

  DCMI_ITConfig(DCMI_IT_LINE, ENABLE);

  DCMI_ITConfig(DCMI_IT_FRAME, ENABLE);

  DCMI_ITConfig(DCMI_IT_OVF, ENABLE);

  DCMI_ITConfig(DCMI_IT_ERR, ENABLE);

  /* Configures the DMA2 to transfer Data from DCMI */

  /* Enable DMA2 clock */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

  /* DMA2 Stream1 Configuration */

  DMA_DeInit(DMA2_Stream1);

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;

  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)theMap;//FSMC_LCD_ADDRESS;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

  DMA_InitStructure.DMA_BufferSize = 9600;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//DMA_MemoryDataSize_HalfWord;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

  //------------------------中断

  DMA_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE);

  DMA_ITConfig(DMA2_Stream1, DMA_IT_HT, ENABLE);

  DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE);

  DMA_ITConfig(DMA2_Stream1, DMA_IT_FE, ENABLE);

  switch(ImageFormat)

  {

  case BMP_QQVGA:

  {

  /* DCMI configuration */

  DCMI_Init(&DCMI_InitStructure);

  /* DMA2 IRQ channel Configuration */

  DMA_Init(DMA2_Stream1, &DMA_InitStructure);

  break;

  }

  case BMP_QVGA:

  {

  /* DCMI configuration */

  DCMI_Init(&DCMI_InitStructure);

  /* DMA2 IRQ channel Configuration */

  DMA_Init(DMA2_Stream1, &DMA_InitStructure);

  break;

  }

  default:

  {

  /* DCMI configuration */

  DCMI_Init(&DCMI_InitStructure);

  /* DMA2 IRQ channel Configuration */

  DMA_Init(DMA2_Stream1, &DMA_InitStructure);

  break;

  }

  }

  }

  其中DCMI_InitStructure.DCMI_CaptureMode这里选用的是DCMI_CaptureMode_SnapShot,没有选用DCMI_CaptureMode_Continuous,因为程序存将图像存SD里,速度有限,只能采一张,存一张。

  所以,只要DCMI_CaptureCmd(ENABLE);DCMI就开始拍照一张,拍完一张后,使能自动关闭。下次要再拍照的时候,重新DCMI_CaptureCmd(ENABLE); 即可。

  然后关于DMA,这

  个配置让我头疼了些时间,主要是对DMA不熟。

  DMA_InitStructure.DMA_Mode 用的是 DMA_Mode_Circular模式,因为DCMI用的是单张拍照的,所以这里用DMA_Mode_Circular模式没有问题。

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设的数据字长,DCMI的寄存器是32位的,所以这里选的word

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存的数据字长,我存图像用的数组是unsigned char类型,所以这里用BYTE。

  DMA_InitStructure.DMA_BufferSize = 9600;//关键是这个,因为摄像头设置的图像大小为120*160的,是RGB565格式,一个像素点占两个字节,所以存一幅图像需要38400字节。而这个buffersize是相对于DMA数据源的字长来说的,这里就是对于DCMI的数据寄存器word类型,38400/4=9600;

  还有关于上位机软件读取这个文件时的方法,我用的是VC+OPENCV,38400字节中,是每两个字节表示一个像素点的RGB颜色,这两个字节是低字节在前,高字节在后,示意方法如下

  for(int i=0; i<Hang*Lie ; i++)

  {

  rgb565 = p[2*i+0] + 256*p[2*i+1];

  b = (rgb565>>0)  & 0x001f;

  g = (rgb565>>5)  & 0x003f;

  r = (rgb565>>11) & 0x001f;

  b = b<<3;

  g = g<<2;

  r = r<<3;

  vec.push_back(b);

  vec.push_back(g);

  vec.push_back(r);

  }

  在OPENCV中存储图像的程序示意如下:

  void vecToImage(vector& vec, IplImage* pImg8u3)

  {

  int cnt=0;

  for(int y=0; yheight; y++)

  {

  unsigned char* ptr = (unsigned char*)(pImg8u3->imageData + y * pImg8u3->widthStep);

  for(int x=0; xwidth; x++)

  {

  *(ptr + 3*x+0) =  vec.at(cnt++);

  *(ptr + 3*x+1) =  vec.at(cnt++);

  *(ptr + 3*x+2) =  vec.at(cnt++);

  }

  }

  }


关键词:存储

版权与免责声明

凡本网注明“出处:维库电子市场网”的所有作品,版权均属于维库电子市场网,转载请必须注明维库电子市场网,https://www.dzsc.com,违反者本网将追究相关法律责任。

本网转载并注明自其它出处的作品,目的在于传递更多信息,并不代表本网赞同其观点或证实其内容的真实性,不承担此类作品侵权行为的直接责任及连带责任。其他媒体、网站或个人从本网转载时,必须保留本网注明的作品出处,并自负版权等法律责任。

如涉及作品内容、版权等问题,请在作品发表之日起一周内与本网联系,否则视为放弃相关权利。

广告
上传BOM文件: BOM文件
*公司名:
*联系人:
*手机号码:
QQ:
应用领域:

有效期:
OEM清单文件: OEM清单文件
*公司名:
*联系人:
*手机号码:
QQ:
有效期:

扫码下载APP,
一键连接广大的电子世界。

在线人工客服

买家服务:
卖家服务:

0571-85317607

客服在线时间周一至周五
9:00-17:30

关注官方微信号,
第一时间获取资讯。

建议反馈

联系人:

联系方式:

按住滑块,拖拽到最右边
>>
感谢您向阿库提出的宝贵意见,您的参与是维库提升服务的动力!意见一经采纳,将有感恩红包奉上哦!