本帖最后由 王昱顺 于 2024-8-30 15:12 编辑
使用PWM触发ADC和DMA-M2M加速CCD摄像头读取
CCD摄像头,就是只有一行像素的摄像头。因为可以获得的信息相对正常的数字摄像头较少。所以极限的传输速度也就可以变得更快。
所以,应该如何利用好STC的硬件外设和DMA功能来加速这一个过程呢?
首先需要讲解的就是PWM触发ADC功能,这个功能仅适用于PWMA组的PWM管脚。通过设置PWMA_CR2,即可设定对应的通道来启动ADC功能。
那么,这种读取方式相对于普通的IO翻转读取有什么好处呢? 最重要的就是不会堵塞其他任务的进行,因为使用IO读取的时候,通常都是使用一个for循环。来将所有的ADC输入存入一个图像数组中。在这个for循环中,其他的代码其实无法运行的。这就对执行效率产生了极大的浪费。 所以,我们可以使用PWM触发ADC+ADC中断存储图像数据的方式。 file:///C:/Users/Administrator/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg 这里我使用的是比较脉冲作为PWM的TRGO(触发输出),来触发ADC。也就是说,除了可以通过设置PWM的周期来确定ADC采样的频率以外,还可以通过设置PWM输出的CCR寄存器来偏移ADC采样点,以避免掉噪声和获得更稳定的ADC数据信息。
经过上面的改进,此时的CCD采样已经可以避免掉长时间的代码执行时间占用问题了。但是采样-处理这个过程中的处理时间仍然占用了相当大的一部分时间。 所以,接下来介绍的就是使用了双缓冲技术的图像读取,并且通过DMA-M2M实现了图像缓冲区和实际的图像操作区域隔离。 先来看一段代码:
adc.c文件
- #include"./H_Group/adc.h"
- u16xdata adc_tmp[128] = {0};
- u16xdata ao_1[128] = {0};
- u16xdata ao_2[128] = {0};
- bitADC_Num = 0; // 标识当前操作的ADC缓冲区
- bitOUT_Flag = 0; // 标识输出缓冲区是否被操作
- u16ADC_Cnt = 0; // ADC缓冲计数
- bitADC_Finish = 0; // 成功置1
- voidStart_Get_Image(void)
- {
- PWMA_CR1 &= ~0x01; // 暂时关闭PWMA
- PWMA_CNTRH = 0x00;
- PWMA_CNTRL = 0x00; // 计数清零
- PWMA_ENO = 0x00; // 关闭PWMA端口输出
- CCD_CLK = 1;
- CCD_SI = 0;
- CCD_CLK = 0;
- CCD_SI = 1;
- CCD_CLK = 1;
- CCD_SI = 0;
- CCD_CLK = 0;
- ADC_START = 1;
- PWMA_ENO = 0x01; // 使能输出,小脉冲
- ADC_Cnt = 0; // 从零开始计数
- ADC_Num = ~ADC_Num; // 取反标志位
- // 判断标志位,启动M2M
- if (OUT_Flag == 0)
- {
- // 空闲状态才允许开始搬运
- DMA_M2M_CFG = 0x80; // r++ = t++
- DMA_M2M_STA = 0x00;
- DMA_M2M_AMT = 0xff; // 设置传输总字节数256
- if (ADC_Num)
- {
- DMA_M2M_TXAH = (u8)((u16)&ao_2>> 8);
- DMA_M2M_TXAL =(u8)((u16)&ao_2);
- }
- else
- {
- DMA_M2M_TXAH = (u8)((u16)&ao_1>> 8);
- DMA_M2M_TXAL =(u8)((u16)&ao_1);
- }
- DMA_M2M_RXAH = (u8)((u16)&adc_tmp>> 8);
- DMA_M2M_RXAL = (u8)((u16)&adc_tmp);
- }
- DMA_M2M_CR = 0xc0; // bit7 1:使能 M2M_DMA, bit6 1:开始 M2M_DMA 操作
- PWMA_CR1 |= 0x01; // 启动PWMA定时器计数
- }
- voidM2M_DMA_Interrupt(void) interrupt 47
- {
- if (DMA_M2M_STA & 0x01) // 发送完成
- {
- DMA_M2M_STA &= ~0x01;
- OUT_Flag = 1; // 标识M2M一次操作完成,等待图像处理完成后置0
- }
- }
- voidADC_Isr(void) interrupt 5
- {
- ADC_FLAG = 0;
- if (ADC_Cnt >= 128)
- {
- PWMA_CR1 &= ~0x01; // 暂时关闭PWMA
- Start_Get_Image(); // 直接开始下一帧的获取
- }
- else
- {
- if (ADC_Num)
- ao_1[ADC_Cnt] = ((u16)ADC_RES<< 8 | ADC_RESL);
- else
- ao_2[ADC_Cnt] = ((u16)ADC_RES<< 8 | ADC_RESL);
- ADC_Cnt++;
- }
- }
复制代码
这部分代码便是ADC.c中的全部程序。其中,ao_1和ao_2为读取的缓冲区,通过ADC_Num切换使用哪个缓冲区来读取。ADC_Cnt为进入中断的计数,通过计数溢出来切换缓冲区的使用。 同时,在下一帧获取起始前,会对空闲的缓冲区进行DMA-M2M复制,以保护读取缓冲区不与图像处理冲突,图像处理通过OUT_Flag进行保护,仅在执行完图像处理后,才允许M2M操作,以防止存储混乱。 这样,只需要保证一帧的时间内,可以完成一帧的图像处理和一帧的图像复制。即可让图像读取变得稳定且连续。
|