查看: 1193|回复: 0

[经验] 用树莓派 Zero 研究 STM32,改造一个计算器

[复制链接]
  • TA的每日心情
    开心
    2019-11-4 13:48
  • 签到天数: 14 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2020-5-20 09:42:57 | 显示全部楼层 |阅读模式
    分享到:
    用树莓派改造 NumWorks 计算器

    MAKER:Zardam/译:趣无尽 Cherry(转载请注明出处)本期我们将用树莓派 Zero 研究 STM32,内容主要包括 DMA 和中断方面,用于改造 NumWorks 计算器 。由于 NumWorks 计算器是由 STM32F412 驱动,那么何不试着把它们放在一起,看看是什么效果。
    如果你希望马上体验 NumWorks 的功能,这里有一个线上的 NumWorks 模拟器:
    https://www.numworks.com/simulator/
    工作原理1、在计算器上添加一个应用程序,该程序将显示树莓派的输出以及从键盘向其发送的按键数据。
    2、在树莓派上通过 SPI 驱动 fbtft 进行显示,并且 NumWorks 的一些裸露的焊盘包含了 SPI 总线。

    效果如下:


    显示来自 SPI 的数据计算器固件已经完成了所有艰苦的底层工作(初始化显示),并提供了用于控制显示的 API。实际上,它是由 FSMC(柔性静态内存控制器)驱动的,因此从 CPU 的角度来看,该显示可通过两个静态地址访问,一个用于命令,一个用于数据,宽度为 16 位。
    对于此应用程序,唯一需要设置的命令,是像素显示区域的命令,它已经在固件中实现。其中,推入像素就像将每个像素依次写入数据地址一样简单,并且它们将会在标准监视器中从左到右,从上到下显示。
    因此,只需要将像素从 SPI 控制器复制到显示地址。对于 DMA 引擎来说,复制像素是非常简单的。
    窗口的设置是在每一帧之前完成的,使用 SPI 总线中未使用的 MISO 引脚作为软件芯片的选择。因此,当 MISO 变低时,触发将中断,设置 SPI 控制器上的软件芯片选择,用于接受传入的数据,并在显示控制器中配置窗口,覆盖整个屏幕。
    设置窗口大约需要 3µs,因此 DMA 的第一个字将延迟到达。在树莓派端,芯片选择和第一个输入字节之间大约有 10 µs 的延迟。
    设置 GPIOSPI 引脚必须设置为第二功能模式,以便在内部连接到 SPI 控制器。MISO 引脚保留为普通 GPIO,因为它用于触发中断。
    1
    2
    3
    4
    5
    GPIOA.MODER()->setMode(5, GPIO::MODER::Mode::AlternateFunction);
    GPIOA.AFR()->setAlternateFunction(5, GPIO::AFR::AlternateFunction::AF5);
    GPIOA.MODER()->setMode(6, GPIO::MODER::Mode::Input);
    GPIOA.MODER()->setMode(7, GPIO::MODER::Mode::AlternateFunction);
    GPIOA.AFR()->setAlternateFunction(7, GPIO::AFR::AlternateFunction::AF5);



    设置 SPI 控制器SPI 控制器的配置非常简单。它设置为 16 位模式,即 RXONLY 和软件芯片选择。MISO 引脚被复用于芯片选择。
    1
    2
    3
    4
    5
    SPI1.CR1()->setRXONLY(true);
    SPI1.CR1()->setSSI(true); // Software chip select
    SPI1.CR1()->setSSM(true); // Software chip select mode
    SPI1.CR1()->setDFF(true); // 16 bits
    SPI1.CR1()->setSPE(true); // enable



    设置 DMA 控制器为了使 DMA 工作,需要选择正确的 DMA 控制器,流和通道。然后配置源地址(此处为 SPI1 数据寄存器),目标地址(显示控制器数据地址),源和目标数据宽度( 16 位),模式(循环)以及编号要传送的数据(此处为 1,因为我们处于循环模式)。无需增加源/目标地址,它始终是相同的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    DMAEngine.SPAR(DMAStream)->set((uint32_t)SPI1.DR()); // Source
    DMAEngine.SM0AR(DMAStream)->set((uint32_t)Ion:isplay:evice:ataAddress); // Destination
    DMAEngine.SNDTR(DMAStream)->set(1); // Number of items
    DMAEngine.SCR(DMAStream)->setCHSEL(3); // SPI Channel
    DMAEngine.SCR(DMAStream)->setDIR(DMA::SCR:irection:eripheralToMemory);
    DMAEngine.SCR(DMAStream)->setMSIZE(DMA::SCR:ataSize::HalfWord);
    DMAEngine.SCR(DMAStream)->setPSIZE(DMA::SCR:ataSize::HalfWord);
    DMAEngine.SCR(DMAStream)->setCIRC(true); // Circular
    DMAEngine.SCR(DMAStream)->setEN(true); // Enable



    设置 SPI 控制器以发出 DMA 请求启用 SPI 控制寄存器中的 RXDMAEN bit 即可完成。
    1
    SPI1.CR2()->setRXDMAEN(true); // enable DMA requests



    通过 MISO 引脚设置中断这个部分设置比较复杂。引脚和实际的中断处理程序之间有多个抽象级别。
    1、将 EXTI(外部中断/事件控制器)配置为触发 NVIC (嵌套矢量中断控制器)中的中断线。
    2、启用 NVIC 线,并定义相应的中断处理程序。不要忘记在处理程序中确认中断。
    1
    2
    3
    4
    SYSCFG.EXTICR2()->setEXTI(Ion::Rpi:evice::ChipSelectPin, Ion::Rpi:evice::ChipSelectGPIO);
    EXTI.RTSR()->set(Ion::Rpi:evice::ChipSelectPin, true);
    EXTI.FTSR()->set(Ion::Rpi:evice::ChipSelectPin, true);
    NVIC.NVIC_ISER0()->set(23, true);



    中断处理程序
    这里有两种情况:
    当 CS 变低时,它将激活 SPI 控制器的软件芯片选择,然后触发显示控制器中窗口的配置。
    当 CS 变高时,它会禁用 SPI 控制器的软件芯片选择。SPI 上收到的任何数据都将被丢弃。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void rpi_isr() {
    EXTI.PR()->set(Ion::Rpi::Device::ChipSelectPin, true);

    if(GPIOA.IDR()->get(6)) {
    SPI1.CR1()->setSSI(true);
    } else {
    Ion::Display::Device::setDrawingArea(KDRect(0,0,320,240), Ion::Display::Device::Orientation:andscape);
    *Ion::Display::Device::CommandAddress = Ion::Display::Device::Command::MemoryWrite;
    SPI1.CR1()->setSSI(false);
    }
    }



    注意:
    如果出现问题,不进行错误处理的话,整个链就可能会被阻塞。这主要发生在 SPI 控制器上。如果DMA 读取数据的速度不够快,它将被卡住,等待错误的确认。
    通过 SPI 总线发送树莓派显示按计划使用原始的 fbtft,但研究代码之后发现不能直接使用,因为它直接访问显示控制器,用于优化像素的推送(通过限制到屏幕上已更改的区域)。但我不想在计算器上实现这种功能,因此我决定自己编写代码。
    https://github.com/notro/fbtft
    通过使用 fbtft 的概念和代码,fbtft 是 Sprite_tm 编写的另一个驱动程序,也是内核内的 vfb 驱动程序,我组装了一个 “quick and dirty” linux 模块,该模块能满足我的需要:将整个帧缓冲区推到 SPI 总线。
    http://spritesmods.com/?art=spitft
    https://github.com/torvalds/linux/blob/ffefb181728f7b97df49ceba18cacfb6c5ee19f2/drivers/video/fbdev/vfb.c
    显示器为 320×240 像素,每个 16bpp,因此每帧为 1228800 位。STM32F412 的最大 SPI 频率为 50MHz,但树莓派无法精确产生。经过测试使用 62.5 MHz,运行良好,因此,理论上最大帧速率为(1228800/(62.5×106))−1 ≈ 50 fps。
    https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
    设置键盘这是计算器上应用程序的最“灵活”的部分(SPI 和 DMA 在后台运行)。计算器只是通过 UART 将固件的键盘扫描例程的结果(64 位位域)发送到树莓派。在树莓派这端,守护程序在 UART 上侦听,并使用 uinput 生成内核的密钥代码。
    https://www.kernel.org/doc/html/v4.12/input/uinput.html
    在 Linux 端使用自定义键映射。我没有采用这种方式,因为仍然可以使用外部蓝牙键盘(而且我不知道是否可以在多个键盘上使用不同的键盘映射)。你可以去试试。
    计算器键盘只有 46 个键,因此为了映射足够的键,按钮 “ x,n,t” 和 “ var” 用于在标准键和数字之间切换。并非标准键盘的所有键都被映射。这一点值得加强。
    鼠标仅依赖于 X.Org 的鼠标仿真。按下电源按钮即可触发。
    https://wiki.archlinux.org/index.php/Xorg/Keyboard_configuration#Enabling_mouse_keys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    Calc          Keymap 1          Keymap 2

    {"left",      {KEY_KP4,         KEY_KP4}},
    {"up",        {KEY_KP8,         KEY_KP8}},
    {"down",      {KEY_KP2,         KEY_KP2}},
    {"right",     {KEY_KP6,         KEY_KP6}},
    {"ok",        {BTN_LEFT,        BTN_LEFT}},
    {"back",      {BTN_RIGHT,       BTN_RIGHT}},
    {"home"},     // not handled here
    {"power"},    // toggle mouse mode
    {NULL},
    {NULL},
    {NULL},
    {NULL},
    {"shift",     {KEY_LEFTSHIFT,   KEY_LEFTSHIFT}},
    {"alpha",     {KEY_CAPSLOCK,    KEY_CAPSLOCK}},
    {"xnt"},      // Switch to first keymap
    {"var"},      // Switch to second keymap
    {"toolbox",   {KEY_RIGHTALT,    KEY_RIGHTALT}},
    {"backspace", {KEY_BACKSPACE,   KEY_ESC}},
    {"A",         {KEY_Q,           KEY_F1}},
    {"B",         {KEY_B,           KEY_F2}},
    {"C",         {KEY_C,           KEY_F3}},
    {"D",         {KEY_D,           KEY_F4}},
    {"E ,",       {KEY_E,           KEY_F5}},
    {"F",         {KEY_F,           KEY_F6}},
    {"G",         {KEY_G,           KEY_F7}},
    {"H",         {KEY_H,           KEY_F8}},
    {"I",         {KEY_I,           KEY_F9}},
    {"J",         {KEY_J,           KEY_F10}},
    {"K",         {KEY_K,           KEY_F11}},
    {"L",         {KEY_L,           KEY_F12}},
    {"M 7",       {KEY_SEMICOLON,   KEY_7}},
    {"N 8",       {KEY_N,           KEY_8}},
    {"O 9",       {KEY_O,           KEY_9}},
    {" (",       {KEY_P,           KEY_5}},
    {"Q )",       {KEY_A,           KEY_MINUS}},
    {NULL},
    {"R 4",       {KEY_R,           KEY_4}},
    {"S 5",       {KEY_S,           KEY_5}},
    {"T 6",       {KEY_T,           KEY_6}},
    {"U *",       {KEY_U,           KEY_KPASTERISK}},
    {"V /",       {KEY_V,           KEY_KPSLASH}},
    {NULL},
    {"W 1",       {KEY_Z,           KEY_1}},
    {"X 2",       {KEY_X,           KEY_2}},
    {"Y 3",       {KEY_Y,           KEY_3}},
    {"Z +",       {KEY_W,           KEY_KPPLUS}},
    {"space -",   {KEY_SPACE,       KEY_KPMINUS}},
    {NULL},
    {"? 0",       {KEY_M,           KEY_0}},
    {"! .",       {KEY_COMMA,       KEY_COMMA}},
    {"x10^x",     {KEY_LEFTCTRL,    KEY_LEFTCTRL}},
    {"ans",       {KEY_LEFTALT,     KEY_LEFTALT}},
    {"exe",       {KEY_ENTER,       KEY_EQUAL}},



    给树莓派供电我首先采用的板子是树莓派 zero,不带 WiFi 版。当以 2.8 V(计算器上的内部稳压电压)供电时,起初工作良好,而且板子上配备了 SD 读卡器和一个晶体管来控制其电源。我决定重新使用 SD 电源板来控制树莓派的电源。

    但之后发现,没有 WiFi 还是不方便,最终还是采用了带 WiFi 的版本。注意,2.8V 的电源不能稳定运行,WiFi 版本需要 3V 电源。禁用 WiFi 芯片(config.txt 中的 “dtoverlay=pi3-disable-wifi”)可以使树莓派在 2.8 V 的情况下正常工作。
    https://www.cypress.com/file/298756/download
    最后,我选用电池直接为树莓派供电。由于无法再使用 SD 卡的占用空间,因此在 SD 推车插槽占用空间的未连接引脚上焊接了晶体管,并以“自由型”方式上拉了电阻。
    我使用了一个 NTR1P02LT1 和一个 10kΩ 电阻,但是任何能够处理至少 100 mA电流的 P 通道 MOSFET 也应该不错。

    电压水平没有问题,因为 STM32 上使用的所有引脚都可承受 5V 电压。
    树莓派会在进入应用程序时启动,并在计算器关闭电源时关闭。因此,可以根据需要离开或进入树莓派应用程序。
    安装计算器计算器很容易放入树莓派。树莓派的连接器所在的位置没有组件。我在 HDMI 接口和计算器的显示接口上用双面胶将其固定到位。
    但还是有点厚,所以原后盖无法使用(切掉了垂直凸片),但可以将它留在原处。



    树莓派的软件配置GitHub 库:
    https://github.com/zardam/spifb
    仅需要安装内核头文件,编译,安装和自动加载模块。
    1
    2
    3
    4
    5
    6
    sudo apt-get install raspberrypi-kernel-headers build-essential
    git clone https://github.com/zardam/spifb.git
    cd spifb
    make -C /lib/modules/$(uname -r)/build M=$PWD
    sudo make -C /lib/modules/$(uname -r)/build M=$PWD modules_install
    sudo depmod -a



    /etc/modules
    1
    2
    3
    spi-bcm2835
    spifb
    uinput



    /boot/config.txt
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    dtparam=spi=on

    # Disable HDMI output, saves some power
    hdmi_blanking=2

    # Enable the mini uart (/dev/ttyS0 on a PI Zero W)
    enable_uart=1

    # Disable LED, saves some power
    dtparam=act_led_trigger=none
    dtparam=act_led_activelow=on



    这里会出现两种可能性:
    直接使用帧缓冲区。这是最简单的方法,但是树莓派 GPU 的硬件加速将不可用。
    使用 fbcp 将普通帧缓冲区(fb0)复制到 SPI 帧缓冲区(spi1)。该副本会带来一些 CPU 开销,但是可以使用硬件加速,并且可以缩放帧缓冲区,因为 320×240 的分辨率几乎无法使用。
    直接使用帧缓冲区
    配置与使用 fbtft 相同。
    /boot/cmdline.txt
    1
    fbcon=map:10



    X Server
    1
    sudo apt-get install xserver-xorg-video-fbdev



    /usr/share/X11/xorg.conf.d/99-fbdev.conf
    1
    2
    3
    4
    5
    Section "Device"  
      Identifier "myfb"
      Driver "fbdev"
      Option "fbdev" "/dev/fb1"
    EndSection



    fbcp
    我使用了分支,用 CMake 来构建。
    https://github.com/Oper8or/rpi-fbcp
    1
    2
    3
    4
    5
    6
    7
    sudo apt-get install cmake
    git clone https://github.com/Oper8or/rpi-fbcp.git
    cd rpi-fbcp
    mkdir build
    cd build
    cmake ..
    make



    /boot/config.txt
    1
    2
    3
    4
    hdmi_force_hotplug=1
    hdmi_cvt=640 480 60 1 0 0 0
    hdmi_group=2
    hdmi_mode=87



    /etc/systemd/system/fbcp.service
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [Unit]
    Description=NumWorks input device
    After=systemd-modules-load.service

    [Service]
    Type=simple
    WorkingDirectory=/home/pi/rpi-fbcp/build
    ExecStart=/home/pi/rpi-fbcp/build/fbcp
    User=root
    Group=root
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target



    启用并启动服务
    1
    2
    3
    sudo systemctl daemon-reload
    sudo systemctl enable fbcp
    sudo systemctl start fbcp



    键盘GitHub 库:
    https://github.com/zardam/uinput-serial-keyboard
    1
    2
    3
    git clone https://github.com/zardam/uinput-serial-keyboard
    cd uinput-serial-keyboard
    gcc uinput.c -o uinput



    需要在 lxde 会话配置中禁用 lxkeymap(只使用 GUI 工具)。
    必须禁用 linux 串行控制台。在 /boot/cmdline.txt 中,删除:
    1
    console=serial0,115200



    /etc/systemd/system/nwinput.service
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [Unit]
    Description=NumWorks input device

    [Service]
    Type=simple
    WorkingDirectory=/home/pi/uinput-serial-keyboard/
    ExecStart=/home/pi/uinput-serial-keyboard/uinput
    User=root
    Group=root
    Restart=on-failure

    [Install]
    WantedBy=multi-user.target



    然后,启用并启动服务:
    1
    2
    3
    sudo systemctl daemon-reload
    sudo systemctl enable nwinput
    sudo systemctl start nwinput



    计算器GitHub 库:
    https://github.com/zardam/epsilon/tree/rpi
    在计算机上,安装 NumWorks SDK 之后:
    1
    2
    3
    git clone -b rpi https://github.com/zardam/epsilon.git
    cd epsilon
    make epsilon_flash



    然后连接并重置计算器,以便刷新自定义固件。
    完成在计算器的浏览器中运行 NumWorks 模拟器。

    现在,整个项目已经完成,希望你喜欢这个项目。
    via : https://zardam.github.io/post/raspberrypi-numworks/

    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-11-18 18:20 , Processed in 0.122148 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.