打印

求助:快速检测GPIO输入脚的电平

[复制链接]
3684|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lzm0117|  楼主 | 2009-5-11 18:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
大家好!我用STM32作摄像头驱动,直接将摄像头数据现在到FSMC接口的LCD上,已经可以工作了,但3百万像素一帧需要2~3秒,想提高速率,问一下除了我下面的方法外,还有没有更快的方法检测输入管脚电平变化?
  1009: { 
  1010:         uint32_t i,j; 
  1011:  
  1012:         union  
  1013:         { 
  1014:                 uint8_t ucBuf[2]; 
  1015:                 uint16_t usData 
  1016:         }U8_U16; 
  1017:  
0x08007C9C B538      PUSH     {r3-r5,lr}
  1018:         __set_PRIMASK(1); 
0x08007C9E 2001      MOVS     r0,#0x01
0x08007CA0 F7F8FA67  BL.W     __set_PRIMASK (0x08000172)
  1019:         LCD_SetCursor(lcdLINE_START, lcdCOLUMN_START); 
0x08007CA4 2100      MOVS     r1,#0x00
0x08007CA6 4608      MOV      r0,r1
0x08007CA8 F7FCF93A  BL.W     LCD_SetCursor (0x08003F20)
  1020:         LCD->LCD_REG = R34;//LCD_WriteRAM_Prepare  
0x08007CAC 2022      MOVS     r0,#0x22
0x08007CAE F04F41D8  MOV      r1,#0x6C000000
0x08007CB2 8008      STRH     r0,[r1,#0x00]
  1021:         while(!((*((volatile unsigned long*)(VSYNC_PORT+GPIOx_IDR)))&VSYNC_PIN));       // 等待VSYNC的上升沿 
0x08007CB4 BF00      NOP      
0x08007CB6 485D      LDR      r0,[pc,#372]  ; @0x08007E2C
0x08007CB8 6800      LDR      r0,[r0,#0x00]
0x08007CBA F4106F00  TST      r0,#0x800
0x08007CBE D0FA      BEQ      0x08007CB6
  1022:         while((*((volatile unsigned long*)(VSYNC_PORT+GPIOx_IDR)))&VSYNC_PIN);  // 等待VSYNC的下降沿 
0x08007CC0 BF00      NOP      
0x08007CC2 485A      LDR      r0,[pc,#360]  ; @0x08007E2C
0x08007CC4 6800      LDR      r0,[r0,#0x00]
0x08007CC6 F4106F00  TST      r0,#0x800
0x08007CCA D1FA      BNE      0x08007CC2
  1023:         for(j=0;j<240;j++) 
  1024:         //for(j=0;j<1536;j++) 
  1025:         { 
0x08007CCC 2500      MOVS     r5,#0x00
0x08007CCE E0A5      B        0x08007E1C
  1026:                 while(!((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN)); // 等待HREF的上升沿 
0x08007CD0 BF00      NOP      
0x08007CD2 4856      LDR      r0,[pc,#344]  ; @0x08007E2C
0x08007CD4 6800      LDR      r0,[r0,#0x00]
0x08007CD6 F4105F00  TST      r0,#0x2000
0x08007CDA D0FA      BEQ      0x08007CD2
  1027:                 while((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN);    // 等待HREF的下降沿 
  1028:  
0x08007CDC BF00      NOP      
0x08007CDE 4853      LDR      r0,[pc,#332]  ; @0x08007E2C
0x08007CE0 6800      LDR      r0,[r0,#0x00]
0x08007CE2 F4105F00  TST      r0,#0x2000
0x08007CE6 D1FA      BNE      0x08007CDE
  1029:                 while(!((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN)); // 等待HREF的上升沿 
0x08007CE8 BF00      NOP      
0x08007CEA 4850      LDR      r0,[pc,#320]  ; @0x08007E2C
0x08007CEC 6800      LDR      r0,[r0,#0x00]
0x08007CEE F4105F00  TST      r0,#0x2000
0x08007CF2 D0FA      BEQ      0x08007CEA
  1030:                 while((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN);    // 等待HREF的下降沿 
  1031:  
0x08007CF4 BF00      NOP      
0x08007CF6 484D      LDR      r0,[pc,#308]  ; @0x08007E2C
0x08007CF8 6800      LDR      r0,[r0,#0x00]
0x08007CFA F4105F00  TST      r0,#0x2000
0x08007CFE D1FA      BNE      0x08007CF6
  1032:                 while(!((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN)); // 等待HREF的上升沿 
0x08007D00 BF00      NOP      
0x08007D02 484A      LDR      r0,[pc,#296]  ; @0x08007E2C
0x08007D04 6800      LDR      r0,[r0,#0x00]
0x08007D06 F4105F00  TST      r0,#0x2000
0x08007D0A D0FA      BEQ      0x08007D02
  1033:                 while((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN);    // 等待HREF的下降沿 
  1034:  
0x08007D0C BF00      NOP      
0x08007D0E 4847      LDR      r0,[pc,#284]  ; @0x08007E2C
0x08007D10 6800      LDR      r0,[r0,#0x00]
0x08007D12 F4105F00  TST      r0,#0x2000
0x08007D16 D1FA      BNE      0x08007D0E
  1035:                 while(!((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN)); // 等待HREF的上升沿 
0x08007D18 BF00      NOP      
0x08007D1A 4844      LDR      r0,[pc,#272]  ; @0x08007E2C
0x08007D1C 6800      LDR      r0,[r0,#0x00]
0x08007D1E F4105F00  TST      r0,#0x2000
0x08007D22 D0FA      BEQ      0x08007D1A
  1036:                 for(i=0;i<320;i++) 
  1037:                 //for(i=0;i<2048;i++) 
  1038:                 { 
  1039:                 /* 
  1040:                         if(!((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN))//HREF为低电平, 无效则退出 
  1041:                         { 
  1042:                                 return; 
  1043:                         } 
  1044:                 */ 
  1045:  
0x08007D24 2400      MOVS     r4,#0x00
0x08007D26 E06F      B        0x08007E08
  1046:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007D28 BF00      NOP      
0x08007D2A 4840      LDR      r0,[pc,#256]  ; @0x08007E2C
0x08007D2C 6800      LDR      r0,[r0,#0x00]
0x08007D2E F0100F01  TST      r0,#0x01
0x08007D32 D0FA      BEQ      0x08007D2A
  1047:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
0x08007D34 BF00      NOP      
0x08007D36 483D      LDR      r0,[pc,#244]  ; @0x08007E2C
0x08007D38 6800      LDR      r0,[r0,#0x00]
0x08007D3A F0100F01  TST      r0,#0x01
0x08007D3E D1FA      BNE      0x08007D36
  1048:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007D40 BF00      NOP      
0x08007D42 483A      LDR      r0,[pc,#232]  ; @0x08007E2C
0x08007D44 6800      LDR      r0,[r0,#0x00]
0x08007D46 F0100F01  TST      r0,#0x01
0x08007D4A D0FA      BEQ      0x08007D42
  1049:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
  1050:  
0x08007D4C BF00      NOP      
0x08007D4E 4837      LDR      r0,[pc,#220]  ; @0x08007E2C
0x08007D50 6800      LDR      r0,[r0,#0x00]
0x08007D52 F0100F01  TST      r0,#0x01
0x08007D56 D1FA      BNE      0x08007D4E
  1051:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007D58 BF00      NOP      
0x08007D5A 4834      LDR      r0,[pc,#208]  ; @0x08007E2C
0x08007D5C 6800      LDR      r0,[r0,#0x00]
0x08007D5E F0100F01  TST      r0,#0x01
0x08007D62 D0FA      BEQ      0x08007D5A
  1052:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
0x08007D64 BF00      NOP      
0x08007D66 4831      LDR      r0,[pc,#196]  ; @0x08007E2C
0x08007D68 6800      LDR      r0,[r0,#0x00]
0x08007D6A F0100F01  TST      r0,#0x01
0x08007D6E D1FA      BNE      0x08007D66
  1053:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007D70 BF00      NOP      
0x08007D72 482E      LDR      r0,[pc,#184]  ; @0x08007E2C
0x08007D74 6800      LDR      r0,[r0,#0x00]
0x08007D76 F0100F01  TST      r0,#0x01
0x08007D7A D0FA      BEQ      0x08007D72
  1054:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
  1055:  
0x08007D7C BF00      NOP      
0x08007D7E 482B      LDR      r0,[pc,#172]  ; @0x08007E2C
0x08007D80 6800      LDR      r0,[r0,#0x00]
0x08007D82 F0100F01  TST      r0,#0x01
0x08007D86 D1FA      BNE      0x08007D7E
  1056:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007D88 BF00      NOP      
0x08007D8A 4828      LDR      r0,[pc,#160]  ; @0x08007E2C
0x08007D8C 6800      LDR      r0,[r0,#0x00]
0x08007D8E F0100F01  TST      r0,#0x01
0x08007D92 D0FA      BEQ      0x08007D8A
  1057:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
0x08007D94 BF00      NOP      
0x08007D96 4825      LDR      r0,[pc,#148]  ; @0x08007E2C
0x08007D98 6800      LDR      r0,[r0,#0x00]
0x08007D9A F0100F01  TST      r0,#0x01
0x08007D9E D1FA      BNE      0x08007D96
  1058:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007DA0 BF00      NOP      
0x08007DA2 4822      LDR      r0,[pc,#136]  ; @0x08007E2C
0x08007DA4 6800      LDR      r0,[r0,#0x00]
0x08007DA6 F0100F01  TST      r0,#0x01
0x08007DAA D0FA      BEQ      0x08007DA2
  1059:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
  1060:  
0x08007DAC BF00      NOP      
0x08007DAE 481F      LDR      r0,[pc,#124]  ; @0x08007E2C
0x08007DB0 6800      LDR      r0,[r0,#0x00]
0x08007DB2 F0100F01  TST      r0,#0x01
0x08007DB6 D1FA      BNE      0x08007DAE
  1061:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007DB8 BF00      NOP      
0x08007DBA 481C      LDR      r0,[pc,#112]  ; @0x08007E2C
0x08007DBC 6800      LDR      r0,[r0,#0x00]
0x08007DBE F0100F01  TST      r0,#0x01
0x08007DC2 D0FA      BEQ      0x08007DBA
  1062:                         U8_U16.ucBuf[1]=(*((volatile unsigned long*)(DATA_PORT+GPIOx_IDR))); 
0x08007DC4 481A      LDR      r0,[pc,#104]  ; @0x08007E30
0x08007DC6 6880      LDR      r0,[r0,#0x08]
0x08007DC8 B2C0      UXTB     r0,r0
0x08007DCA F88D0001  STRB     r0,[sp,#0x01]
  1063:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
0x08007DCE BF00      NOP      
0x08007DD0 4816      LDR      r0,[pc,#88]  ; @0x08007E2C
0x08007DD2 6800      LDR      r0,[r0,#0x00]
0x08007DD4 F0100F01  TST      r0,#0x01
0x08007DD8 D1FA      BNE      0x08007DD0
  1064:                         while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿 
0x08007DDA BF00      NOP      
0x08007DDC 4813      LDR      r0,[pc,#76]  ; @0x08007E2C
0x08007DDE 6800      LDR      r0,[r0,#0x00]
0x08007DE0 F0100F01  TST      r0,#0x01
0x08007DE4 D0FA      BEQ      0x08007DDC
  1065:                         U8_U16.ucBuf[0]=(*((volatile unsigned long*)(DATA_PORT+GPIOx_IDR))); 
0x08007DE6 4812      LDR      r0,[pc,#72]  ; @0x08007E30
0x08007DE8 6880      LDR      r0,[r0,#0x08]
0x08007DEA B2C0      UXTB     r0,r0
0x08007DEC F88D0000  STRB     r0,[sp,#0x00]
  1066:                         LCD->LCD_RAM = U8_U16.usData; 
0x08007DF0 F8BD0000  LDRH     r0,[sp,#0x00]
0x08007DF4 F04F41D8  MOV      r1,#0x6C000000
0x08007DF8 8048      STRH     r0,[r1,#0x02]
  1067:                         while((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN);    // 等待PCLK的下降沿 
  1068:                 } 
0x08007DFA BF00      NOP      
0x08007DFC 480B      LDR      r0,[pc,#44]  ; @0x08007E2C
0x08007DFE 6800      LDR      r0,[r0,#0x00]
0x08007E00 F0100F01  TST      r0,#0x01
0x08007E04 D1FA      BNE      0x08007DFC
  1036:                 for(i=0;i<320;i++) 
0x08007E06 1C64      ADDS     r4,r4,#1
0x08007E08 F5B47FA0  CMP      r4,#0x140
0x08007E0C D38C      BCC      0x08007D28
  1069:                 while((*((volatile unsigned long*)(HREF_PORT+GPIOx_IDR)))&HREF_PIN);    // 等待HREF的下降沿 
  1070:         } 
0x08007E0E BF00      NOP      
0x08007E10 4806      LDR      r0,[pc,#24]  ; @0x08007E2C
0x08007E12 6800      LDR      r0,[r0,#0x00]
0x08007E14 F4105F00  TST      r0,#0x2000
0x08007E18 D1FA      BNE      0x08007E10
  1023:         for(j=0;j<240;j++) 
0x08007E1A 1C6D      ADDS     r5,r5,#1
0x08007E1C 2DF0      CMP      r5,#0xF0
0x08007E1E F4FFAF57  BCC.W    0x08007CD0
  1071:         __set_PRIMASK(0);               
0x08007E22 2000      MOVS     r0,#0x00
0x08007E24 F7F8F9A5  BL.W     __set_PRIMASK (0x08000172)
  1072: }
0x08007E28 BD38      POP      {r3-r5,pc}
0x08007E2A 0000      MOVS     r0,r0
0x08007E2C 0C08      LSRS     r0,r1,#16
0x08007E2E 4001      ANDS     r1,r1,r0
0x08007E30 1000      ASRS     r0,r0,#0
0x08007E32 4001      ANDS     r1,r1,r0
沙发
香水城| | 2009-5-11 22:37 | 只看该作者

看不懂你的程序,请写一下操作过程

使用特权

评论回复
板凳
lzm0117|  楼主 | 2009-5-12 09:24 | 只看该作者

不好意思,没描述清楚

就是要检测GPIO脚的电平变化,因为需要很快的速度,我在处理的时候将中断全部关闭,然后用查询的方法,比如摄像头每个时钟的上升沿输出一个像素的数据,我现在系统时钟为72MHz,测试只能采集到2点几Mhz的时钟,高于这个会出现漏采集现象,有没有更好的方法快速检测电平

uint32_t i;
uint16_t usData[2048];

for(i=0;i<2048;i++)//一行数据
{
  while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR))) &PCLK_PIN)); // 等待PCLK的上升沿
 usData=(*((volatile unsigned long*)(DATA_PORT+GPIOx_IDR)));
 while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿
}
 
 

使用特权

评论回复
地板
lzm0117|  楼主 | 2009-5-12 09:37 | 只看该作者

输入数据寄存器能不能用位域操作?

我原来用流明的,记得它有两个位操作区,SRAM和外设的低1M,而STM32我看到有SRAM区的位操作,及写GPIO端口的位操作,GPIOx_BSRR及GPIOx_BRR,不知读GPIOx_IDR能否读某位?
  while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR))) &PCLK_PIN)); // 等待PCLK的上升沿

的汇编后产生这么几条指令

0x08007DA0 BF00      NOP      
0x08007DA2 4822      LDR      r0,[pc,#136]  ; @0x08007E2C
0x08007DA4 6800      LDR      r0,[r0,#0x00]
0x08007DA6 F0100F01  TST      r0,#0x01
0x08007DAA D0FA      BEQ      0x08007DA2

这样大概用10几个时钟周期,上升沿加下降沿就得花20来个,
如果用中断进进出出的可能更不行吧

使用特权

评论回复
5
ijk| | 2009-5-12 10:16 | 只看该作者

输入数据寄存器可以用位域操作

  输入数据寄存器可以用位域操作,速度应该更快点。
我最近也用了摄像头。我的建议是:可以用GPIO来检测VSYNC-因为它比较慢(每秒几十帧),但不建议用GPIO来检测HSYNC(即HREF)-因为它很快、大概在几十us;建议用HSYNC来作为触发信号,可以用来触发DMA。

使用特权

评论回复
6
lzm0117|  楼主 | 2009-5-12 10:46 | 只看该作者

TO ijk

你好!我想问你一下,你的数据读取是怎么同步的能,我用MCO脚来驱动CMOS摄像头的工作时钟XCLK,由摄像头来产生像素时钟PCLK,然后检测每一个PCLK开保存图像数据,你用DMA方式吗?时钟怎么同步?我用示波器看分频后的PCLK不是均等方波。
另外我试了一下,判断电平用位域操作和字节操作指令差不多,唯一的差别在于比较一个用CMP,一个用TST

定义
#define PH_BASE       0x40000000
#define PH_BB_BASE    0x42000000
#define Var_GetBit_BB(VarAddr, BitNumber)       
          (*(__IO uint32_t *) (PH_BB_BASE | ((VarAddr - PH_BASE) << 5) | ((BitNumber) << 2)))

while(!Var_GetBit_BB(PCLK_PORT+GPIOx_IDR,PCLK_PIN));
//汇编成如下: 
0x08007DB4 BF00      NOP      
0x08007DB6 481C      LDR      r0,[pc,#112]  ; @0x08007E28
0x08007DB8 6800      LDR      r0,[r0,#0x00]
0x08007DBA 2800      CMP      r0,#0x00
0x08007DBC D0FB      BEQ      0x08007DB6


 while(!((*((volatile unsigned long*)(PCLK_PORT+GPIOx_IDR)))&PCLK_PIN)); // 等待PCLK的上升沿
 
//汇编成如下:

0x08007D24 BF00      NOP      
0x08007D26 483F      LDR      r0,[pc,#252]  ; @0x08007E24
0x08007D28 6800      LDR      r0,[r0,#0x00]
0x08007D2A F0100F01  TST      r0,#0x01
0x08007D2E D0FA      BEQ      0x08007D26

使用特权

评论回复
7
ijk| | 2009-5-12 14:30 | 只看该作者

采样PCLK

  采样PCLK,我用的芯片跟你的不同,我的芯片直接有摄像头接口,所以不需要用软件方式检测PCLK。但是,我用的芯片跟LCD也是用外部总线(类似于STM32 FSMC),摄像头接口和LCD之间用由 DMA来进行数据传输的,中间没有用RAM来暂存数据,我想你用 STM32也同样可以用DMA来进行数据传输,只是DMA的源是GPIO的数据寄存器,DMA的目的是 RAM 而已。

使用特权

评论回复
8
lzm0117|  楼主 | 2009-5-12 14:47 | 只看该作者

用DMA传输的问题

    谢谢ijk的建议,我上午调试了一下,用软件检测PCLK极限就2MHZ左右,而这样采集一帧QXGA图像需要2秒钟左右,必须用DMA传输方式提高速度,我的想法是这样,有PCLK输出36MHZ的像素时钟,再用DMA方式保存数据到SRAM中,但问题有两个:
    DMA支持存储器与存储器之间传输及外设与存储器之间传输,但外设好像是指定的那几个,不知支持gpio的端口输入寄存器作为源地址吗?
    需要用行同步信号HREF作为触发DMA传输的信号,问能否支持GPIO输入上升沿作为DMA触发信号?
    另外,如果处理器主频是72MHZ,DMA的频率是多少?会不会被其他打断呢?
    请香主及各位大侠给我一些建议,3Q

使用特权

评论回复
9
ijk| | 2009-5-12 15:57 | 只看该作者

STM32 DMA的速度

  STM32 DMA的速度,如果没有记错,是4个HCLK可以进行一次传输,另外DMA的优先级比CPU低。所以满打满算,DMA可以支持最快18M的 PCLK,留一些余量的话,12M的 PCLK应该有希望。
  用行同步信号HREF作为触发DMA传输的信号,应该没有问题,我大概看了一下,可以把HREF接到某个定时器的输入,然后用定时器的输入作为DMA的触发信号。
  gpio的端口输入寄存器作为源地址,对于STM32应该也没有问题,这一点试一下很容易的。

使用特权

评论回复
10
lonely8122| | 2009-5-13 15:40 | 只看该作者

位寻址区

用位寻址区检测某IO口状态应该比用字寻址区的快

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

8

帖子

0

粉丝