12下一页
返回列表 发新帖我要提问本帖赏金: 100.00元(功能说明)

[STM32F4] 公司“砍掉”FIFO和晶振预算怎么办?教你这样巧开发STM32...

[复制链接]
5920|27
 楼主| 呐咯密密 发表于 2021-2-7 17:21 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂
在之前的公司做过一个被压缩成本的0V7670无FIFO无晶振的拍照项目,主控使用STM32F407,确实节约了成本,但是没有FIFO确实很麻烦,因为FIFO可以暂存图像数据,有这颗芯片可以降低单片机对高速IO的限制,还节省CPU资源,但是没有也只能搞下去。
接线:
说起OV7670必须说接线,特别是阉割版本的模块,不然真的会头大,在接线这一步自己就因为接错,导致我耽误了很长时间。
因为没有FIFO,所以这里使用F407的DCMI接口。
因为是测试,所以直接用杜邦线连接,这个线简直头疼,太多太乱。这里给一下我的接线表和我的实物图。这里已经是能再LCD上面显示图片了。
28898601f973ec2b17.png 65763601f97d7a78a2.png
另外还有两个接口,一个是POWER DOWN控制信号(PG9),一个是复位控制信号(PG15)。

PWDN    |    输入         |    POWER DOWN模式选择           0:工作               1:POWER DOWN

RESET   |   输入         |    初始化所有寄存器到默认值         0:RESET 模式  1:一般模式


外设初始化:
用到摄像头,还要拍照,这一快屏幕是一定少不了的,先从初始化LCD屏幕开始。
  1. //初始化lcd
  2. //该初始化函数可以初始化各种ILI93XX液晶,但是其他函数是基于ILI9320的!!!
  3. //在其他型号的驱动芯片上没有测试!
  4. void LCD_Init(void)
  5. {         
  6.         vu32 i=0;
  7.         
  8.   GPIO_InitTypeDef  GPIO_InitStructure;
  9.         FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
  10.   FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;
  11.         FSMC_NORSRAMTimingInitTypeDef  writeTiming;
  12.         
  13.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟  
  14.   RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟  
  15.         

  16.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推挽输出,控制背光
  17.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  18.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  19.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
  20.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  21.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 //PB15 推挽输出,控制背光
  22.         
  23.   GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,15 AF OUT
  24.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
  25.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  26.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  27.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  28.   GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化  
  29.         
  30.   GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);//PE7~15,AF OUT
  31.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
  32.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  33.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  34.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  35.   GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化  

  36.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
  37.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
  38.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  39.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  40.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  41.   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化  

  42.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
  43.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
  44.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  45.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  46.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  47.   GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化

  48.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
  49.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
  50.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
  51.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);
  52.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC);
  53.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
  54.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
  55.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
  56.   GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12

  57.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
  58.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
  59.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
  60.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
  61.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
  62.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
  63.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
  64.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
  65.   GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12

  66.   GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
  67.   GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);


  68.   readWriteTiming.FSMC_AddressSetupTime = 0XF;         //地址建立时间(ADDSET)为16个HCLK 1/168M=6ns*16=96ns        
  69.   readWriteTiming.FSMC_AddressHoldTime = 0x00;         //地址保持时间(ADDHLD)模式A未用到        
  70.   readWriteTiming.FSMC_DataSetupTime = 60;                        //数据保存时间为60个HCLK        =6*60=360ns
  71.   readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
  72.   readWriteTiming.FSMC_CLKDivision = 0x00;
  73.   readWriteTiming.FSMC_DataLatency = 0x00;
  74.   readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;         //模式A
  75.    

  76.         writeTiming.FSMC_AddressSetupTime =9;              //地址建立时间(ADDSET)为9个HCLK =54ns
  77.   writeTiming.FSMC_AddressHoldTime = 0x00;         //地址保持时间(A               
  78.   writeTiming.FSMC_DataSetupTime = 8;                 //数据保存时间为6ns*9个HCLK=54ns
  79.   writeTiming.FSMC_BusTurnAroundDuration = 0x00;
  80.   writeTiming.FSMC_CLKDivision = 0x00;
  81.   writeTiming.FSMC_DataLatency = 0x00;
  82.   writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;         //模式A


  83.   FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;//  这里我们使用NE4 ,也就对应BTCR[6],[7]。
  84.   FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
  85.   FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM;  //SRAM   
  86.   FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit   
  87.   FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
  88.   FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
  89.         FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
  90.   FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;   
  91.   FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;  
  92.   FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;        //  存储器写使能
  93.   FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;   
  94.   FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序
  95.   FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
  96.   FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //读写时序
  97.   FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming;  //写时序

  98.   FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC配置

  99.   FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  // 使能BANK1
  100.                
  101.          delay_ms(50); // delay 50 ms
  102.          LCD_WriteReg(0x0000,0x0001);
  103.         delay_ms(50); // delay 50 ms
  104.           lcddev.id = LCD_ReadReg(0x0000);   
  105.            if(lcddev.id<0XFF||lcddev.id==0XFFFF||lcddev.id==0X9300)//读到ID不正确,新增lcddev.id==0X9300判断,因为9341在未被复位的情况下会被读成9300
  106.         {        
  107.                  //尝试9341 ID的读取               
  108.                 LCD_WR_REG(0XD3);                                   
  109.                 lcddev.id=LCD_RD_DATA();        //dummy read         
  110.                  lcddev.id=LCD_RD_DATA();        //读到0X00
  111.                   lcddev.id=LCD_RD_DATA();           //读取93                                                                  
  112.                  lcddev.id<<=8;
  113.                 lcddev.id|=LCD_RD_DATA();          //读取41                                       
  114.                  if(lcddev.id!=0X9341)                //非9341,尝试是不是6804
  115.                 {        
  116.                          LCD_WR_REG(0XBF);                                   
  117.                         lcddev.id=LCD_RD_DATA();         //dummy read         
  118.                          lcddev.id=LCD_RD_DATA();           //读回0X01                           
  119.                          lcddev.id=LCD_RD_DATA();         //读回0XD0                                   
  120.                           lcddev.id=LCD_RD_DATA();        //这里读回0X68
  121.                         lcddev.id<<=8;
  122.                           lcddev.id|=LCD_RD_DATA();        //这里读回0X04         
  123.                         if(lcddev.id!=0X6804)                //也不是6804,尝试看看是不是NT35310
  124.                         {
  125.                                 LCD_WR_REG(0XD4);                                   
  126.                                 lcddev.id=LCD_RD_DATA();//dummy read  
  127.                                 lcddev.id=LCD_RD_DATA();//读回0X01         
  128.                                 lcddev.id=LCD_RD_DATA();//读回0X53        
  129.                                 lcddev.id<<=8;         
  130.                                 lcddev.id|=LCD_RD_DATA();        //这里读回0X10         
  131.                                 if(lcddev.id!=0X5310)                //也不是NT35310,尝试看看是不是NT35510
  132.                                 {
  133.                                         LCD_WR_REG(0XDA00);        
  134.                                         lcddev.id=LCD_RD_DATA();                //读回0X00         
  135.                                         LCD_WR_REG(0XDB00);        
  136.                                         lcddev.id=LCD_RD_DATA();                //读回0X80
  137.                                         lcddev.id<<=8;        
  138.                                         LCD_WR_REG(0XDC00);        
  139.                                         lcddev.id|=LCD_RD_DATA();                //读回0X00               
  140.                                         if(lcddev.id==0x8000)lcddev.id=0x5510;//NT35510读回的ID是8000H,为方便区分,我们强制设置为5510
  141.                                         if(lcddev.id!=0X5510)                        //也不是NT5510,尝试看看是不是SSD1963
  142.                                         {
  143.                                                 LCD_WR_REG(0XA1);
  144.                                                 lcddev.id=LCD_RD_DATA();
  145.                                                 lcddev.id=LCD_RD_DATA();        //读回0X57
  146.                                                 lcddev.id<<=8;         
  147.                                                 lcddev.id|=LCD_RD_DATA();        //读回0X61        
  148.                                                 if(lcddev.id==0X5761)lcddev.id=0X1963;//SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963
  149.                                         }
  150.                                 }
  151.                         }
  152.                  }         
  153.         }
  154.         if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510||lcddev.id==0X1963)//如果是这几个IC,则设置WR时序为最快
  155.         {
  156.                 //重新配置写时序控制寄存器的时序                                                                        
  157.                 FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零         
  158.                 FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
  159.                 FSMC_Bank1E->BWTR[6]|=3<<0;                //地址建立时间(ADDSET)为3个HCLK =18ns           
  160.                 FSMC_Bank1E->BWTR[6]|=2<<8;         //数据保存时间(DATAST)为6ns*3个HCLK=18ns
  161.         }else if(lcddev.id==0X6804||lcddev.id==0XC505)        //6804/C505速度上不去,得降低
  162.         {
  163.                 //重新配置写时序控制寄存器的时序                                                                        
  164.                 FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零         
  165.                 FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
  166.                 FSMC_Bank1E->BWTR[6]|=10<<0;        //地址建立时间(ADDSET)为10个HCLK =60ns           
  167.                 FSMC_Bank1E->BWTR[6]|=12<<8;         //数据保存时间(DATAST)为6ns*13个HCLK=78ns
  168.         }
  169.          printf(" LCD ID:%x\r\n",lcddev.id); //打印LCD ID   
这里参考了正点原子的LCD的初始化,我说贴上的代码没有具体型号的屏幕初始化,因为代码太长,会占用我的篇幅,在我的上面贴的代码最后是打印LCD ID,打印完成后会进行if()语句的判断,判断属于哪个型号的LCD屏幕,再进行初始化,大家有需要可以参考正点原子的初始化。在初始化完成后会对LCD进行清屏。因为使用摄像头会占用大量的内存资源,所有我在板子上贴了一片外部SARM芯片,同样需要对其进行初始化。此过程已放入LCD初始化中。
  1. LCD_Display_Dir(0);                //默认为竖屏
  2.         LCD_LED=1;                                //点亮背光
  3.         LCD_Clear(WHITE);
exfuns初始化:
在使用摄像头拍照结束后,会将.bmp文件存入内存卡中,所以这里需要初始化exfuns,为fatfs相关变量申请内存 。
  1. u8 exfuns_init(void)
  2. {
  3.         u8 i;
  4.         for(i=0;i<_VOLUMES;i++)
  5.         {
  6.                 fs[i]=(FATFS*)mymalloc(SRAMIN,sizeof(FATFS));        //为磁盘i工作区申请内存        
  7.                 if(!fs[i])break;
  8.         }
  9.         file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));                //为file申请内存
  10.         ftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));                //为ftemp申请内存
  11.         fatbuf=(u8*)mymalloc(SRAMIN,512);                                //为fatbuf申请内存
  12.         if(i==_VOLUMES&&file&&ftemp&&fatbuf)return 0;  //申请有一个失败,即失败.
  13.         else return 1;        
  14. }
OV7670初始化:
初始化OV7670主要是初始化POWER DOWN控制信号(PG9)和复位控制信号(PG15),以及SCCB的接口,该接口是串行摄像机控制总线协议的英文名简称,相当于一个简易的I2C协议,同时也会初始化QVGA的分辨率。
  1. //初始化OV7670
  2. //返回0:成功
  3. //返回其他值:错误代码
  4. u8 OV7670_Init(void)
  5. {
  6. u16 i=0;
  7. u16 reg=0;
  8. u8 temp=0;
  9. //设置IO
  10. GPIO_InitTypeDef GPIO_InitStructure;

  11. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
  12. //GPIOG9,15初始化设置
  13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;//PG9,15推挽输出
  14. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
  15. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  16. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
  17. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  18. GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
  19. OV7670_PWDN=0;        //POWER ON
  20. delay_ms(10);
  21. OV7670_RST=0;        //复位OV7670
  22. delay_ms(10);
  23. OV7670_RST=1;        //结束复位
  24. SCCB_Init(); //初始化SCCB 的IO口        
  25. SCCB_WR_Reg(0X12, 0x80);        //软复位OV7670
  26. delay_ms(50);
  27. LED0=0;
  28. //初始化 OV7670,采用QVGA分辨率(320*240)
  29. for(i=0;i<sizeof(ov7670_init_reg_tbl)/sizeof(ov7670_init_reg_tbl[0]);i++)
  30. {
  31. SCCB_WR_Reg(ov7670_init_reg_tbl[i][0],ov7670_init_reg_tbl[i][1]);
  32. }
  33. return 0x00; //ok
  34. }

  35. //初始化SCCB接口
  36. void SCCB_Init(void)
  37. {                                
  38.   GPIO_InitTypeDef  GPIO_InitStructure;

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

  47.         GPIO_SetBits(GPIOD,GPIO_Pin_6|GPIO_Pin_7);
  48.         SCCB_SDA_OUT();           
  49. }        
DCMI配置:

这里是一个重点,因为我的OV7670不带FIFO和晶振,而7670的CMOS芯片的时钟可以高达24M,而单片机的IO速度不够,退而求其次,使用DCMI的接口,勉强让其跑起来,当然,如果是ARM9或者DSP图像处理芯片就另说了,人家内存大,带camera接口,但是价格也感人。
DCMI接口是一个同步并行接口,能够接收外部 8 位、 10 位、 12 位或 14 位 CMOS 摄像头模块发出的高速数据流。可支持不同的数据格式: YCbCr4:2:2/RGB565 逐行视频和压缩数据 (JPEG)。  

DCMI可以接收54M的数据流,高达14根数据线和一条像素时钟线PIXCLK,且像素时钟的极性可编程,可以自定义在上升沿还是下降沿捕捉数据。
  1. //DCMI初始化
  2. void My_DCMI_Init(void)
  3. {
  4.   GPIO_InitTypeDef  GPIO_InitStructure;
  5.         NVIC_InitTypeDef NVIC_InitStructure;

  6.         
  7.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA B C E 时钟
  8.         RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);//使能DCMI时钟
  9.   //PA4/6初始化设置
  10.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//PA4/6   复用功能输出
  11.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能输出
  12.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  13.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  14.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  15.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
  16.         
  17.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;// PB6/7   复用功能输出
  18.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
  19.         
  20.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;//PC6/7/8/9/11 复用功能输出
  21.   GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化        

  22.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//PE5/6  复用功能输出
  23.   GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化        

  24.         GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13  DCMI_HSYNC
  25.         GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13  DCMI_PCLK  
  26.          GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13  DCMI_VSYNC
  27.          GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13  DCMI_D0  
  28.          GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13  DCMI_D1
  29.         GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13  DCMI_D2
  30.         GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13  DCMI_D3
  31.         GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
  32.         GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13  DCMI_D5
  33.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13  DCMI_D6
  34.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13  DCMI_D7

  35.         
  36.         DCMI_DeInit();//清除原来的设置


  37.   DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;//连续模式
  38.         DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;//全帧捕获
  39.         DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;//8位数据格式  
  40.         DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low;//HSYNC 低电平有效
  41.         DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Falling;//PCLK 上升沿有效
  42.         DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;//硬件同步HSYNC,VSYNC
  43.         DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_High;//VSYNC 低电平有效
  44.         DCMI_Init(&DCMI_InitStructure);
  45.         printf("start DCMI_IT_FRAME\r\n");
  46.         DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断
  47.         //DCMI_ITConfig(DCMI_IT_LINE,ENABLE); //开启行中断
  48.         //DCMI_ITConfig(DCMI_IT_VSYNC,ENABLE); //开启场中断        
  49.         DCMI_Cmd(ENABLE);        //DCMI使能
  50.         printf("ENABLE DCMI_IT_FRAME OK\r\n");
  51.   NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
  52.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级1
  53.         NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;                //子优先级3
  54.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
  55.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器、
  56.         printf("My_DCMI_Init OK\r\n");
  57. }
DCMI的初始化中我们使用连续模式的全帧捕捉,在中断中我们选择帧中断,方便获取一个满屏的图像数据。
DCMI获取到的数据会保存在32位的数据寄存器DCMI_DR中,之后我们便可以通过DMA进行传输,图像的缓冲由DMA进行管理,不是由DCMI接管。
DCMI的DMA配置:
  1. void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u32 DMA_Memory1BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
  2. {
  3.         DMA_InitTypeDef  DMA_InitStructure;
  4.         NVIC_InitTypeDef NVIC_InitStructure;
  5.         
  6.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
  7.         DMA_DeInit(DMA2_Stream1);//等待DMA2_Stream1
  8.         while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
  9.         
  10.   /* 配置 DMA Stream */
  11.   DMA_InitStructure.DMA_Channel = DMA_Channel_1;  //通道1 DCMI通道
  12.   DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR
  13.   DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//DMA 存储器0地址
  14.   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式
  15.   DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;//数据传输量

  16.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
  17.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;//存储器增量模式
  18.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据长度:32位
  19.   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;//存储器数据长度
  20.   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式
  21.   DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
  22.   DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO模式        
  23.   DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全FIFO
  24.   DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
  25.   DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
  26.   DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化DMA Stream
  27.                
  28.         if(DMA_Memory1BaseAddr)
  29.   {
  30.                 DMA_DoubleBufferModeCmd(DMA2_Stream1,ENABLE);//双缓冲模式
  31.           DMA_MemoryTargetConfig(DMA2_Stream1,DMA_Memory1BaseAddr,DMA_Memory_1);//配置目标地址1
  32.         }        
  33.         DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);//开启传输完成中断
  34.         
  35.         NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
  36.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
  37.         NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;                //子优先级0
  38.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
  39.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器、        
  40. }


开始工作:
在启动MCU后需要初始化相关配置。
  1.   LCD_Init();                                        //LCD初始化  
  2.         FSMC_SRAM_Init();                        //初始化外部SRAM.
  3.         W25QXX_Init();                                //初始化W25Q128
  4.         exfuns_init();                                //为fatfs相关变量申请内存  
  5. //  
  6. //        KEY_Init();                                        //按键初始化
  7. //        SD_Init();
  8.         //TIM3_Int_Init(10000-1,8400-1);//10Khz计数,1秒钟中断一次
  9.         TIM1_PWM_Init();

  10.         my_mem_init(SRAMIN);                //初始化内部内存池
  11.         my_mem_init(SRAMEX);                //初始化内部内存池  
  12.         my_mem_init(SRAMCCM);                //初始化CCM内存池
  13.          usmart_dev.init(84);                //初始化USMART
  14.          POINT_COLOR=RED;//设置字体为红色         
  15.                 LCD_ShowString(30,130,200,16,16,"OV7670 00089");
  16.                 printf("init ov\n");
  17.         while(OV7670_Init())//初始化OV7670
  18.         {
  19.                 LCD_ShowString(30,130,240,16,16,"OV7670 ERR");
  20.                 delay_ms(200);
  21.           LCD_Fill(30,130,239,170,WHITE);
  22.                 delay_ms(200);
  23.         }
  24.         LCD_ShowString(30,130,200,16,16,"OV7670 OK");
  25.         printf("init ov ok\n");
  26.         delay_ms(1500);        

  27.         jpeg_buf0=mymalloc(SRAMIN,jpeg_dma_bufsize*4);        //为jpeg dma接收申请内存        
  28.         jpeg_buf1=mymalloc(SRAMIN,jpeg_dma_bufsize*4);        //为jpeg dma接收申请内存        
  29.         jpeg_data_buf=mymalloc(SRAMEX,300*1024);                //为jpeg文件申请内存(最大300KB)
  30.          pname=mymalloc(SRAMIN,30);//为带路径的文件名分配30个字节的内存         
  31.          while(pname==NULL||!jpeg_data_buf)        //内存分配出错
  32.          {            
  33.                 LCD_ShowString(30,190,240,16,16,"内存分配失败!");
  34.                         printf("jpeg_data_buf ERROR!!!!!!!!!!!!!!!!!!!!!!! ok\n");
  35.                 delay_ms(200);                                 
  36.                 LCD_Fill(30,190,240,146,WHITE);//清除显示            
  37.                 delay_ms(200);                                 
  38.         }
  39.     printf("init mymalloc ok    \n");
  40.         My_DCMI_Init();                        //DCMI配置
  41. //        DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,10,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);//DCMI DMA配置  
  42. //        DCMI_Start();                 //启动传输

  43.         printf("init My_DCMI_Init ok\n");
  44.         dcmi_rx_callback=jpeg_dcmi_rx_callback;//回调函数
  45.         //DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,10,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);//DCMI DMA配置  
  46.         DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Enable);//DCMI DMA配置(双缓冲模式)
  47.         printf("init DCMI_Start");
  48.         DCMI_Start();                         //启动传输
  49.   OV7670_Window_Set(12,176,240,320);//OV7670设置输出窗口        
  50.         //        sw_ov2640_mode();                //切换为OV2640模式
  51. //while(1){
  52. //        if(sd_ok==1){
  53.         printf("start DCMI_Start ok ");
  54.         dcmi=1;
  55.         while(jpeg_data_ok!=1);        //等待第一帧图片采集完
  56.         dcmi=0;
  57.         DCMI_Stop(); //停止DMA搬运
  58.         printf("DCMI STOP");
  59.         
  60.         sw_sdcard_mode();        //切换为SD卡模式
  61.         printf("SWICCT SDCARD SUCCESS");
  62. //                if(KEY0==0)        //BMP拍照
  63. //                {
  64.   f_mount(fs[0],"0:",1);                 //挂载SD卡  
  65.         res=f_mkdir("0:/PHOTO");                //创建PHOTO文件夹
  66.         if(res!=FR_EXIST&&res!=FR_OK)         //发生了错误
  67.         {                 
  68.                 printf("SD CARD ERROR");
  69.                 LCD_ShowString(30,190,200,16,16,"内存分配失败!");
  70.                 delay_ms(200);                                 
  71.                 sd_ok=0;         
  72.         }
  73.         printf("SD CARD RIGHT");
  74.         camera_new_pathname(pname,0);//得到文件名        
  75.         printf("CREAT PNAME SUCCESS");
  76.         res=bmp_encode(pname,0,0,lcddev.width,lcddev.height,0);
  77.         printf("BMP SAVE SUCCESS");
  78.                 delay_ms(200);

此过程皆在main函数中完成,初始化DCMI之前必须先初始化内存并为jpeg dma接收申请和分配内存。
然后启动DMA,启动DCMI,进行图像采集。
  1. //DCMI,启动传输
  2. void DCMI_Start(void)
  3. {
  4.         LCD_Scan_Dir(U2D_L2R);                   //从上到下,从左到右        
  5.         LCD_Set_Window(0,0,240,320); //LCD设置显示窗口,如果改变了分辨率,这里需要更改
  6.         LCD_SetCursor(0,0);  
  7.         LCD_WriteRAM_Prepare();                        //开始写入GRAM
  8.         DMA_Cmd(DMA2_Stream1, ENABLE);//开启DMA2,Stream1
  9.         DCMI_CaptureCmd(ENABLE);//DCMI捕获使能  
  10. }
设置OV7670输出窗口        
  1. //设置图像输出窗口
  2. //对QVGA设置。
  3. void OV7670_Window_Set(u16 sx,u16 sy,u16 width,u16 height)
  4. {
  5.         u16 endx;
  6.         u16 endy;
  7.         u8 temp;
  8.         endx=sx+width*2;        //V
  9.          endy=sy+height*2;
  10.         if(endy>784)endy-=784;
  11.         temp=SCCB_RD_Reg(0X03);                                //读取Vref之前的值
  12.         temp&=0XF0;
  13.         temp|=((endx&0X03)<<2)|(sx&0X03);
  14.         SCCB_WR_Reg(0X03,temp);                                //设置Vref的start和end的最低2位
  15.         SCCB_WR_Reg(0X19,sx>>2);                        //设置Vref的start高8位
  16.         SCCB_WR_Reg(0X1A,endx>>2);                        //设置Vref的end的高8位

  17.         temp=SCCB_RD_Reg(0X32);                                //读取Href之前的值
  18.         temp&=0XC0;
  19.         temp|=((endy&0X07)<<3)|(sy&0X07);
  20.         SCCB_WR_Reg(0X32,temp);
  21.         SCCB_WR_Reg(0X17,sy>>3);                        //设置Href的start高8位
  22.         SCCB_WR_Reg(0X18,endy>>3);                        //设置Href的end的高8位
  23. }
此时DCMI启动,会抓拍照片,当捕获到一帧完整数据会触发DCMI的帧中断,进入中断函数之后会进行JPEG的数据处理,在处理JPEG数据时会停止DMA的传输,并将数据保存到pbuf[]数组。
  1. //DCMI中断服务函数
  2. void DCMI_IRQHandler(void)
  3. {
  4.         if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)//捕获到一帧图像
  5.         {
  6.                 //DCMI_Stop(); //停止DMA搬运

  7.                 DCMI_ClearITPendingBit(DCMI_IT_FRAME);//清除中断        
  8.                 if (dcmi == 1)
  9.                         //printf("dcmi == 1 \r\n");                        
  10.                         jpeg_data_process();
  11.                 //ov_frame++;
  12.                
  13.         }
  14.         
  15. }

  16. //处理JPEG数据
  17. //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
  18. void jpeg_data_process(void)
  19. {
  20.         u16 i;
  21.         u16 rlen;//剩余数据长度
  22.         u32 *pbuf;

  23.         if(jpeg_data_ok==0)        //jpeg数据还未采集完?
  24.         {
  25.                 DMA_Cmd(DMA2_Stream1,DISABLE);                //停止当前传输
  26.                 while(DMA_GetCmdStatus(DMA2_Stream1) != DISABLE);        //等待DMA2_Stream1可配置
  27.                 rlen=jpeg_dma_bufsize-DMA_GetCurrDataCounter(DMA2_Stream1);//得到剩余数据长度        
  28.                 pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾,继续添加
  29.                 if(DMA2_Stream1->CR&(1<<19))
  30.                         for(i=0;i<rlen;i++)
  31.                                 pbuf[i]=jpeg_buf1[i];//读取buf1里面的剩余数据
  32.                 else
  33.                         for(i=0;i<rlen;i++)
  34.                                 pbuf[i]=jpeg_buf0[i];//读取buf0里面的剩余数据
  35.                 jpeg_data_len+=rlen;                        //加上剩余长度
  36.                 jpeg_data_ok=1;                                 //标记JPEG数据采集完按成,等待其他函数处理
  37.         }
  38.         if(jpeg_data_ok==2)        //上一次的jpeg数据已经被处理了
  39.         { DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_dma_bufsize);//传输长度为jpeg_buf_size*4字节
  40.                 DMA_Cmd(DMA2_Stream1,ENABLE); //重新传输
  41.                 jpeg_data_ok=0;                                        //标记数据未采集
  42.                 jpeg_data_len=0;                                //数据重新开始
  43.         }
  44.                         printf("jpeg_data_process success \r\n");               
  45. }
当判断一帧数据采集完成,会启动DMA传输,传输完成会触发DMA中断,最后模式切换到SD卡模式,进行图片的保存。
  1. void DMA2_Stream1_IRQHandler(void)
  2. {        
  3.         if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)==SET)//DMA2_Steam1,传输完成标志
  4.         {  
  5.                  DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);//清除传输完成中断
  6.      dcmi_rx_callback();        //执行摄像头接收回调函数,读取数据等操作在这里面处理  
  7.                  
  8.         }                                                                                             
  9. }

  10. //jpeg数据接收回调函数
  11. void jpeg_dcmi_rx_callback(void)
  12. {
  13.         u16 i;
  14.         u32 *pbuf;
  15.         pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾
  16.         if(DMA2_Stream1->CR&(1<<19))//buf0已满,正常处理buf1
  17.         {
  18.                 for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf0[i];//读取buf0里面的数据
  19.                 jpeg_data_len+=jpeg_dma_bufsize;//偏移
  20.         }else //buf1已满,正常处理buf0
  21.         {
  22.                 for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf1[i];//读取buf1里面的数据
  23.                 jpeg_data_len+=jpeg_dma_bufsize;//偏移
  24.         }         
  25. }
我这里保存的是BMP格式,所以有一个转码处理,这里也贴一下。
  1. //BMP编码函数
  2. //将当前LCD屏幕的指定区域截图,存为16位格式的BMP文件 RGB565格式.
  3. //保存为rgb565则需要掩码,需要利用原来的调色板位置增加掩码.这里我们已经增加了掩码.
  4. //保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.
  5. //filename:存放路径
  6. //x,y:在屏幕上的起始坐标  
  7. //mode:模式.0,仅仅创建新文件的方式编码;1,如果之前存在文件,则覆盖之前的文件.如果没有,则创建新的文件.
  8. //返回值:0,成功;其他,错误码.  
  9. u8 bmp_encode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 mode)
  10. {               
  11.         u32 i=0,num=0;
  12.         u32* pbuf;
  13.         u16* pbuf_Data;
  14.         FIL* f_bmp;
  15.         u16 bmpheadsize;                        //bmp头大小                    
  16.          BITMAPINFO hbmp;                        //bmp头         
  17.         u8 res=0;
  18.         u16 tx,ty;                                           //图像尺寸
  19.         u16 *databuf;                                //数据缓存区地址                  
  20.         u16 pixcnt;                                           //像素计数器
  21.         u16 bi4width;                               //水平像素字节数           
  22.         if(width==0||height==0)return PIC_WINDOW_ERR;        //区域错误
  23.         if((x+width-1)>lcddev.width)return PIC_WINDOW_ERR;                //区域错误
  24.         if((y+height-1)>lcddev.height)return PIC_WINDOW_ERR;        //区域错误
  25.          
  26. #if BMP_USE_MALLOC == 1        //使用malloc        
  27.         databuf=(u16*)pic_memalloc(1024);                //开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.
  28.         if(databuf==NULL)return PIC_MEM_ERR;                //内存申请失败.
  29.         f_bmp=(FIL *)pic_memalloc(sizeof(FIL));        //开辟FIL字节的内存区域
  30.         if(f_bmp==NULL)                                                                //内存申请失败.
  31.         {                 
  32.                 pic_memfree(databuf);
  33.                 return PIC_MEM_ERR;                                
  34.         }         
  35. #else
  36.         databuf=(u16*)bmpreadbuf;
  37.         f_bmp=&f_bfile;
  38. #endif              
  39.         bmpheadsize=sizeof(hbmp);//得到bmp文件头的大小   
  40.         mymemset((u8*)&hbmp,0,sizeof(hbmp));//置零空申请到的内存.            
  41.         hbmp.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);//信息头大小
  42.         hbmp.bmiHeader.biWidth=width;                 //bmp的宽度
  43.         hbmp.bmiHeader.biHeight=height;         //bmp的高度
  44.         hbmp.bmiHeader.biPlanes=1;                         //恒为1
  45.         hbmp.bmiHeader.biBitCount=16;                 //bmp为16位色bmp
  46.         hbmp.bmiHeader.biCompression=BI_BITFIELDS;//每个象素的比特由指定的掩码决定。
  47.          hbmp.bmiHeader.biSizeImage=hbmp.bmiHeader.biHeight*hbmp.bmiHeader.biWidth*hbmp.bmiHeader.biBitCount/8;//bmp数据区大小
  48.                                     
  49.         hbmp.bmfHeader.bfType=((u16)'M'<<8)+'B';//BM格式标志
  50.         hbmp.bmfHeader.bfSize=bmpheadsize+hbmp.bmiHeader.biSizeImage;//整个bmp的大小
  51.            hbmp.bmfHeader.bfOffBits=bmpheadsize;//到数据区的偏移

  52.         hbmp.RGB_MASK[0]=0X00F800;                         //红色掩码
  53.         hbmp.RGB_MASK[1]=0X0007E0;                         //绿色掩码
  54.         hbmp.RGB_MASK[2]=0X00001F;                         //蓝色掩码

  55.         if(mode==1)res=f_open(f_bmp,(const TCHAR*)filename,FA_READ|FA_WRITE);//尝试打开之前的文件
  56.          if(mode==0||res==0x04)res=f_open(f_bmp,(const TCHAR*)filename,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件                  
  57.          if((hbmp.bmiHeader.biWidth*2)%4)//水平像素(字节)不为4的倍数
  58.         {
  59.                 bi4width=((hbmp.bmiHeader.biWidth*2)/4+1)*4;//实际要写入的宽度像素,必须为4的倍数.        
  60.         }else bi4width=hbmp.bmiHeader.biWidth*2;                //刚好为4的倍数         
  61.          if(res==FR_OK)//创建成功
  62.         {
  63.                 res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部  

  64.                 pixcnt=0;
  65.                 pbuf=(u32*)jpeg_data_buf;
  66. //                pbuf_Data = (u32*)jpeg_data_buf;

  67.         for (i = 16; i < 32; i++)
  68.         {
  69.                 pbuf[i]=0x0;
  70. //                pbuf_Data=pbuf&0x0000FFFF;

  71.         }
  72.                 f_write(f_bmp,(u32*)pbuf,jpeg_data_len,&bw);
  73.                
  74.                 f_close(f_bmp);
  75.         }
  76. //         if(res==FR_OK)//创建成功
  77. //        {
  78. //                res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部  
  79. //                for(ty=y+height-1;hbmp.bmiHeader.biHeight;ty--){
  80. //                        
  81. //                }
  82. //                pixcnt=0;
  83. //                pbuf=(u32*)jpeg_data_buf;
  84. //                f_write(f_bmp,(u32*)pbuf,jpeg_data_len*4,&bw);
  85. //               
  86. //                f_close(f_bmp);
  87. //        }               
  88. #if BMP_USE_MALLOC == 1        //使用malloc        
  89.         pic_memfree(databuf);         
  90.         pic_memfree(f_bmp);                 
  91. #endif        
  92.         return res;
  93. }
因为项目不是现在做的,所以讲起来可能有些乱,这里附上百度网盘的下载链接,因为文件过大,没法上传附件,望谅解。
链接的隐藏是为了获取回复量,有点私心,各位大侠高抬贵手。感激不尽。
游客,如果您要查看本帖隐藏内容请回复

打赏榜单

21小跑堂 打赏了 100.00 元 2021-03-04
理由:恭喜通过原创文章审核!请多多加油哦!

xyz549040622 发表于 2021-2-7 22:10 | 显示全部楼层
坐个沙发支持一下。

评论

感谢支持,祝大佬新年快乐,牛年大吉  发表于 2021-2-8 11:16
感谢支持,祝大佬新年快乐,牛年大吉  发表于 2021-2-8 11:16
goyhuan 发表于 2021-2-8 10:56 | 显示全部楼层
xiaoqizi 发表于 2021-3-2 18:53 | 显示全部楼层
好想看楼主的pose啊

评论

这个可不行哦  发表于 2021-3-3 09:31
木木guainv 发表于 2021-3-2 18:54 | 显示全部楼层
效果真的很不错啊
磨砂 发表于 2021-3-2 18:55 | 显示全部楼层
分辨率能达到多少啊
晓伍 发表于 2021-3-2 18:58 | 显示全部楼层
代码的解释很详细啊
八层楼 发表于 2021-3-2 19:00 | 显示全部楼层
正好可以拿来用用啊 谢谢
keke 发表于 2021-3-6 14:01 | 显示全部楼层

看看楼主的pose啊
crazyren 发表于 2021-3-8 14:00 | 显示全部楼层
看看...
zhouyong77 发表于 2021-3-8 19:00 来自手机 | 显示全部楼层
谢谢楼主分享经验
qslt1983 发表于 2021-3-9 09:35 | 显示全部楼层
高手,学习学习。
gyh974 发表于 2021-3-9 09:43 | 显示全部楼层
楼主牛,成本确实是很关键,能省一点是一点
自己的灌饼 发表于 2021-3-9 11:38 | 显示全部楼层
楼主厉害,成本是关键指标。
触觉的爱 发表于 2021-3-9 19:02 | 显示全部楼层
就差成品效果 图展示了
wandersky 发表于 2021-3-10 19:03 | 显示全部楼层
mark stm32驱动摄像头。
yangbinge 发表于 2021-3-11 09:06 | 显示全部楼层
支持一下
wjzhe 发表于 2021-3-11 11:14 | 显示全部楼层
支持一下
YDMCP 发表于 2021-3-13 07:09 | 显示全部楼层
牛 谢谢
ehua 发表于 2021-3-14 08:15 | 显示全部楼层
如果您要查看本帖隐藏内容请回复
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

568

主题

4085

帖子

56

粉丝
快速回复 在线客服 返回列表 返回顶部