打印
[应用方案]

准备用M484输出VGA信号,全程DMA,不需要MCU参与

[复制链接]
770|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
vsfopen|  楼主 | 2020-4-10 22:36 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 vsfopen 于 2020-4-10 23:06 编辑

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

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

    vsf_protect_t orig = vsf_protect_int();
        TIMER0->CTL     |= TIMER_CTL_CNTEN_Msk;
        TIMER1->CTL     |= TIMER_CTL_CNTEN_Msk;
        TIMER2->CTL     |= TIMER_CTL_CNTEN_Msk;
        TIMER3->CTL     |= TIMER_CTL_CNTEN_Msk;
        while (1);
    vsf_unprotect_int(orig);
    return 0;
}


直接上波形:

蓝色是8位像素的最低位
绿色是行同步信号
黄色是场同步信号
紫色是场消隐信号

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

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

这里需要依赖这个SA寄存器的递增,因为M484 ram不够放下640 * 480的画面,所以实际使用320 * 240,这样,一行需要输出2次。
实现原理就是通过特殊方法,用PDMA实现一个硬件状态机。
状态0的时候,读取并保存SA:
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);
    desc[1].src         = (uint32_t)&PDMA->DSCT[9].SA;
    desc[1].dest        = (uint32_t)&vga.line_buffer;
状态2的时候,恢复原来的SA:
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);
    desc[1].src         = (uint32_t)&vga.line_buffer;
    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 | 只看该作者
修改了一下,场消隐的时候,不需要关行同步

使用特权

评论回复
板凳
vsfopen|  楼主 | 2020-4-11 13:26 | 只看该作者
本帖最后由 vsfopen 于 2020-4-12 13:58 编辑

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


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

void PDMA_IRQHandler(void)
{
    if (PDMA->TDSTS & (1 << 15)) {
        PDMA->DSCT[9].SA += sizeof(vga_buffer[0]);
        PDMA->TDSTS = 1 << 15;
    }
}
这里,第一行一个像素是高电平,其他都是低电平。
波形上重复2次是做分辨率减半,因为屏幕的实际分辨率是640 X 480,而缓冲是320 X 240。
中断代码里,只是模拟SA自增,因为貌似硬件没自增,只能软件处理,否则都不需要中断。

使用特权

评论回复
地板
antusheng| | 2020-4-11 22:10 | 只看该作者
这图好酷啊,什么示波器

使用特权

评论回复
5
vsfopen|  楼主 | 2020-4-12 13:56 | 只看该作者
antusheng 发表于 2020-4-11 22:10
这图好酷啊,什么示波器

就是普通平板示波器

使用特权

评论回复
6
vsfopen|  楼主 | 2020-4-12 15:04 | 只看该作者

最后卡在这里,M484貌似对PDMA的触发频率有限制:
https://bbs.21ic.com/icview-2940770-1-1.html

使用特权

评论回复
7
vsfopen|  楼主 | 2020-4-12 17:08 | 只看该作者

搞定,通过降低分辨率,降低了PDMA的触发频率,目前不漏数据了。

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

484的2个问题(也不是问题,因为使用上比较极限):
1. PDMA 18M触发下,会漏触发。这个问题比较关键,限制了显示分辨率。
2. PDMA的SA寄存器,并没有递增,即使设置了SAINC,估计是内部的地址寄存器递增。这个不是关键问题,用很少的CPU资源,可以弥补。CPU在DMA中断里,模拟递增就行了。

使用特权

评论回复
8
wangshujun| | 2020-4-13 18:05 | 只看该作者
很好呀,其实驱动vga现在用的机会比较少了,但是驱动不带控制器的屏甚至led大屏之类的很有实用价值

使用特权

评论回复
9
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分辨率。


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

使用特权

评论回复
10
wangshujun| | 2020-4-14 12:01 | 只看该作者
vsfopen 发表于 2020-4-13 22:41
前两天还有人问我,用这种方式实现硬件控制屏幕,因为484不带屏幕控制器。
其实可以实现的,但是代码比GPI ...

以前在lpc的arm7上用dma+spi定时器驱动做过单色vga驱动,数据量比你的小多了,显示挺稳定的

使用特权

评论回复
11
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

粉丝