树莓派+8x32的MAX7219点阵模块显示字符串

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Parallel2333/article/details/88358368

https://blog.csdn.net/zyaiwmy/article/details/70224250?utm_source=blogxgwz1

按照上面这篇文章进行配置了驱程,修改了一下示例代码,效果如下:

这里记一下项目的github地址:https://github.com/rm-hull/luma.led_matrix

下面为按自己需求实现的代码

import re
import time
import argparse

from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop
from luma.core.render import canvas
from luma.core.virtual import viewport
from luma.core.legacy import text, show_message
from luma.core.legacy.font import proportional, CP437_FONT, TINY_FONT, SINCLAIR_FONT, LCD_FONT


def demo(str):
    # create matrix device
    serial = spi(port=0, device=0, gpio=noop())
    device = max7219(serial, cascaded=4, block_orientation=-90, rotate=0)
    
    print("Created device")
    
    with canvas(device) as draw:
        text(draw, (3,0), str, fill="white", font=proportional(LCD_FONT))
    time.sleep(300)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='matrix_demo arguments',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('--str', type=str, default='hello', help='The string you want to show')

    args = parser.parse_args()

    try:
        demo(args.str)
    except KeyboardInterrupt:
        pass

上面是luma.led_matrix的使用,前几天认真的百度了一下它的源码,这里记录几个重点:一个是内存映射机制,一个是缓冲协议,还有一个就是串口通信协议。

内存映射机制要讲的话就得从操作系统开始讲起,每个外设接入到Linux系统上以及树莓派上时会在/dev/中产生一个设备文件,并且会根据驱动程序中定义的datasheet将外设的寄存器、缓冲区等信息映射到Linux内核空间中。这样就相当于写好驱程的外部设备一旦插入我们的系统上,我们的系统就会提供专门的地址空间给这个设备。拿这个MAX7219模块来说,一旦插上就说明我们可以通过写Linux内存的缓冲区以及控制寄存器的读写来控制这个外部设备。讲完外设怎么映射到系统内存中,接下来就是怎么在系统内存中找到这块内存地址并对其进行IO。
寻址就需要提一下mmap,这个是内存映射的函数。操作系统有虚拟地址空间、物理地址空间和磁盘空间,我们找内存地址是通过CPU寻址,在以前还没有引入虚拟内存的概念时都是通过控制物理内存地址通过查询物理页表进行磁盘IO的,之后由于物理内存不够了就引入了虚拟内存,这个技术让每个进程都有4GB的空间进行读写。在只有物理内存的时候CPU总线发出的地址是物理地址,但引入虚拟内存后CPU总线发出的地址是虚拟地址,之后MMU单元会接手这个虚拟地址并转换为物理地址后,接着做上面提到的只有物理内存时候的事。所以CPU是不知道自己发出的地址是物理地址还是虚拟地址的,只知道自己发出了要寻找的地址。mmap函数是将磁盘地址映射到虚拟内存的,不经过物理内存,但虚拟内存寻址的时候是需要访问物理内存再到磁盘的。之所以引入物理内存是因为磁盘的IO太浪费时间了,物理内存能基本提供几个进程的空间,让操作系统使用的时候直接访问物理内存即可,而不用频繁的与磁盘进行IO,而引入虚拟内存又是因为随着计算机的发展进程越来越多也越来越大,一个物理内存已经很难为这么多的进程提供舒适的空间了。综上是操作系统的一些概念了。

還有DMA:一个完整的DMA传输过程需要经过DMA请求、DMA响应、DMA传输、DMA结束。
通过硬件为RAM与I/O设备开辟一条直接传送数据的通道,DMA使用时CPU是不能控制和使用内存的(這里我還是有疑惑的)。获得CPU的响应之后DMA发出内存地址和控制信号,然後进行传送,传送结束。如果是写寄存器的话就需要DMA传输需要配置要传输的buf长度,源地址就是buf的长度,目标地址就是寄存器地址,DMA使用的是总线地址。所以USB里当有数据需要传输时我们又不能一直等设备,就只好DMA在有I/O需求的时候调用,由DMA控制系统总线。 

python的缓存协议,python内置对象中支持缓存协议得有bytes和bytearray,str通过encode转化为bytes,bytes是byte的序列而str是unicode的序列。网络上传输因为需要使用二进制流所以需要转化将本身的str转化为bytes从而在网络上传输。提到缓存协议是因为luma.core中是使用了PIL初始化MAX7219模块的缓存区,所以我们在MAX7219上打印东西就相当于在空白图像上画画一样。还有一种是NumPy风格的,也就是用了PIL中的Image.new(mode,size)这种方式初始化缓存。https://github.com/pyserial/pyserial/tree/master/serial
https://github.com/doceme/py-spidev/blob/master/spidev_module.c
SPI接口是有发送缓存区的,通过TXBMT判断发送缓存区是否为空,置位时表示数据已经进入移位寄存器开始发送。
SPIF是发送完成后的中断标志位,数据发送完毕后硬件置位,如果中断允许则进入中断服务程序。
也就是说先发送缓存区,再移位寄存器。

串口通信协议主要就是定义了一种数据传输格式,比如实际数据和控制数据,传输的时候跟socket通信挺相像的,还可以定义异步传输(asyncio.Protocol),定义好了传输然后就是读写+编解码,也就是也就是个通信协议,不同之处在于串口的连接方法了。我们平时用的BLCA(bind/listen/connect/accept)是同步的,也就是说会阻塞知道有数据传进来,这是一种比较低级的控制方法了。再高级一点就是select/poll/epoll,这种方式可以监听fd_set,有数据来了再处理,通过客户端套接字和服务器端套接字的缓存内容判断是否有新连接还是老客户,这种异步可以让CPU在没有数据到来的时候干别的事,不会被IO阻塞了。再高级的就是asyncio,我们平时调用asyncio有一个概念叫协程,也就是在一个线程内部的协程类似于一个进程内的多线程,使用async/await来实现,await其实也就是yield from,就是说在一个服务器内有很多IO操作时不用等待这个IO返回再进行下一个操作,而是可以让这个IO在后台等待,这个时候CPU可以去处理下一个协程,比如await asyncio.sleep(5),就是说可以去做别的事(等待5秒这件事,可以替换成别的事件),一旦收到了IO结果就立即回到await之前的协程,就算只sleep了3秒也要回去。
还有就是事件循环,我们需要先创建事件循环,然后定义协程,之后再将协程放到事件循环当中运行即可。多个协程任务不用等到这个协程任务做完才做下一个,而是可以在做前一个协程的阻塞过程中运行下一个协程。

 

展开阅读全文

没有更多推荐了,返回首页