[应用方案] 准备用M484输出VGA信号,全程DMA,不需要MCU参与

[复制链接]
 楼主| vsfopen 发表于 2020-4-10 22:36 | 显示全部楼层 |阅读模式
本帖最后由 vsfopen 于 2020-4-10 23:06 编辑

M484的PDMA功能上相当强悍,可以用这个来实现一些时序,甚至硬件状态机

目前实现的测试代码里,配置完TIMER,PWM,PDMA,GPIO之后,基本只是启动4个定时器:
  1. int main(void)
  2. {
  3.     ........

  4.     vsf_protect_t orig = vsf_protect_int();
  5.         TIMER0->CTL     |= TIMER_CTL_CNTEN_Msk;
  6.         TIMER1->CTL     |= TIMER_CTL_CNTEN_Msk;
  7.         TIMER2->CTL     |= TIMER_CTL_CNTEN_Msk;
  8.         TIMER3->CTL     |= TIMER_CTL_CNTEN_Msk;
  9.         while (1);
  10.     vsf_unprotect_int(orig);
  11.     return 0;
  12. }


直接上波形:
20200209031037.png 20200209031051.png
蓝色是8位像素的最低位
绿色是行同步信号
黄色是场同步信号
紫色是场消隐信号

全部都是DMA自动生成的,用了比较特殊的实现方式。
可能新塘原来设计PDMA的时候,也不一定发现可以这么玩。

目前遇到一个问题,行像素输出的时候,没有递增。
用SWREQ测试的时候,可以看到DSCT里的CTL的TXCNT一直在递减,但是SA并没有递增(CTL里设置了Source递增,波形上也可以看到,像素信号高电平只有一个)。
  1. uint8_t vga_buffer[240][320 + 1] = {
  2.     {0xFF},
  3. };

这里需要依赖这个SA寄存器的递增,因为M484 ram不够放下640 * 480的画面,所以实际使用320 * 240,这样,一行需要输出2次。
实现原理就是通过特殊方法,用PDMA实现一个硬件状态机。
状态0的时候,读取并保存SA:
  1. desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
  2.     desc[1].src         = (uint32_t)&PDMA->DSCT[9].SA;
  3.     desc[1].dest        = (uint32_t)&vga.line_buffer;
状态2的时候,恢复原来的SA:
  1. desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
  2.     desc[1].src         = (uint32_t)&vga.line_buffer;
  3.     desc[1].dest        = (uint32_t)&PDMA->DSCT[9].SA;
但是,这个依赖SA的递增。
目前卡在这里,有官方的技术支持,可以回答一下问题不:
https://bbs.21ic.com/icview-2940368-1-1.html
 楼主| vsfopen 发表于 2020-4-10 23:18 | 显示全部楼层
修改了一下,场消隐的时候,不需要关行同步
20200209033343.png
 楼主| vsfopen 发表于 2020-4-11 13:26 | 显示全部楼层
本帖最后由 vsfopen 于 2020-4-12 13:58 编辑

有人确认DMA地址自增的问题前,先用中断的方式:
20200209174045.png

  1. uint8_t vga_buffer[240][320 + 1] = {
  2.     {0xFF},
  3. };

  4. void PDMA_IRQHandler(void)
  5. {
  6.     if (PDMA->TDSTS & (1 << 15)) {
  7.         PDMA->DSCT[9].SA += sizeof(vga_buffer[0]);
  8.         PDMA->TDSTS = 1 << 15;
  9.     }
  10. }
这里,第一行一个像素是高电平,其他都是低电平。
波形上重复2次是做分辨率减半,因为屏幕的实际分辨率是640 X 480,而缓冲是320 X 240。
中断代码里,只是模拟SA自增,因为貌似硬件没自增,只能软件处理,否则都不需要中断。
antusheng 发表于 2020-4-11 22:10 | 显示全部楼层
这图好酷啊,什么示波器
 楼主| vsfopen 发表于 2020-4-12 13:56 | 显示全部楼层
antusheng 发表于 2020-4-11 22:10
这图好酷啊,什么示波器

就是普通平板示波器
 楼主| vsfopen 发表于 2020-4-12 15:04 | 显示全部楼层
VGA.jpg
最后卡在这里,M484貌似对PDMA的触发频率有限制:
https://bbs.21ic.com/icview-2940770-1-1.html
 楼主| vsfopen 发表于 2020-4-12 17:08 | 显示全部楼层
VGA_OK.jpg
搞定,通过降低分辨率,降低了PDMA的触发频率,目前不漏数据了。

分辨率只有行像素 200,列像素300。一个像素是长方形的,如果要改为正方形的话,会变为200X150的分辨率。
粗看还行,细看就算了。。。
估计要换STM32了,STM32的LTDC设计上,就考虑了兼容VGA。

484的2个问题(也不是问题,因为使用上比较极限):
1. PDMA 18M触发下,会漏触发。这个问题比较关键,限制了显示分辨率。
2. PDMA的SA寄存器,并没有递增,即使设置了SAINC,估计是内部的地址寄存器递增。这个不是关键问题,用很少的CPU资源,可以弥补。CPU在DMA中断里,模拟递增就行了。
wangshujun 发表于 2020-4-13 18:05 | 显示全部楼层
很好呀,其实驱动vga现在用的机会比较少了,但是驱动不带控制器的屏甚至led大屏之类的很有实用价值
 楼主| vsfopen 发表于 2020-4-13 22:41 | 显示全部楼层
本帖最后由 vsfopen 于 2020-4-13 22:43 编辑
wangshujun 发表于 2020-4-13 18:05
很好呀,其实驱动vga现在用的机会比较少了,但是驱动不带控制器的屏甚至led大屏之类的很有实用价值 ...

前两天还有人问我,用这种方式实现硬件控制屏幕,因为484不带屏幕控制器。
其实可以实现的,但是代码比GPIO实现的版本复杂很多。
可以认为是用VHDL写的。。。

VGA的话,也只是玩玩而已,基本都做好了。
由于触发性能问题,也只能做到200X300或者200X150分辨率。
VGA.jpg

好在MCU外面只需要几个电阻实现R/2R DA网络。

wangshujun 发表于 2020-4-14 12:01 | 显示全部楼层
vsfopen 发表于 2020-4-13 22:41
前两天还有人问我,用这种方式实现硬件控制屏幕,因为484不带屏幕控制器。
其实可以实现的,但是代码比GPI ...

以前在lpc的arm7上用dma+spi定时器驱动做过单色vga驱动,数据量比你的小多了,显示挺稳定的
 楼主| vsfopen 发表于 2020-4-14 14:22 | 显示全部楼层
wangshujun 发表于 2020-4-14 12:01
以前在lpc的arm7上用dma+spi定时器驱动做过单色vga驱动,数据量比你的小多了,显示挺稳定的 ...

单色的简单,用SPI就行了,甚至如果SPI多的话,可以用3路SPI,1主2从。SPI做到18M很简单的。
但是,8位色的话,要用定时器触发DMA,包括网上看到的STM32的方案也一样,触摸频率貌似都有限制。

最好的方法,还是STM32的LDTC,兼容VGA时序。
后面可能会换成这个方式。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

325

帖子

8

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