导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页
    1. 主页
    2. steward
    3. 帖子
    • 资料
    • 关注 0
    • 粉丝 9
    • 我的积分 4563
    • 主题 2
    • 帖子 93
    • 最佳 40
    • 群组 0

    steward 发布的帖子

    • 回复: Gaviar Handheld (小志掌機)

      接著讓我們來玩玩 ucGUI,編譯步驟如下:

      $ cd
      $ git clone https://github.com/steward-fu/ucgui
      $ cd ucgui
      $ make MOD=gaviar
      $ ls -al libucgui.a
          -rw-r--r-- 1 steward steward 6633760 Sep 20 13:54 libucgui.a
      

      main.c

      #include "GUI.h"
      #include "TEXT.h"
      #include "FRAMEWIN.h"
          
      int main(int argc, char *argv[])
      {
          TEXT_Handle hText = 0;
          FRAMEWIN_Handle hFrame = 0;
          
          GUI_Init();
          GUI_SetBkColor(GUI_GRAY);
          GUI_Clear();
         
          hFrame = FRAMEWIN_Create("main", 0, WM_CF_SHOW, 10, 10, 300, 200);
          FRAMEWIN_AddCloseButton(hFrame, FRAMEWIN_BUTTON_RIGHT, 0);
          FRAMEWIN_AddMaxButton(hFrame, FRAMEWIN_BUTTON_RIGHT, 0);
          FRAMEWIN_AddMinButton(hFrame, FRAMEWIN_BUTTON_RIGHT, 0);
          FRAMEWIN_SetText(hFrame, "main");
          FRAMEWIN_SetFont(hFrame, &GUI_Font8x16);
        
          hText = TEXT_CreateAsChild(20, 100, 200, 30, hFrame, 100, WM_CF_SHOW, "", TEXT_CF_HCENTER);
          TEXT_SetText(hText, "Hello, Gaviar Handheld !");
          TEXT_SetFont(hText, &GUI_Font8x16);
        
          FRAMEWIN_SetActive(hFrame, 1);
          GUI_Delay(1000);
          FRAMEWIN_Delete(hFrame);
          
          GUI_Delay(30000);
          return 0;
      }
      

      編譯

      $ export PATH=/opt/gaviar/bin:$PATH
      $ riscv64-linux-gcc main.c -o main libucgui.a -IGUI_X -IGUI/Core -IGUI/Widget -IGUI/WM -lSDL
      

      完成
      1.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      雖說將 rootfs 解壓縮到 MicroSD 是一個相當方便、簡單的作法,不過當 MicroSD 的資料正在寫回時,如果不小心斷電或者拔卡,則會很容易造成卡崩的問題,因此,比較好的作法是將磁區改成唯讀的方式,避免卡崩,不過這樣還是浪費一個磁區,所以改用 SQUASHFS 檔案是一個最佳的方案,因為將 rootfs 壓縮成一個檔案除了可以節省空間,更可以方便替換 rootfs,可是 Kernel 在開機時,要如何掛載這個 SQUASHFS 檔案呢?一般常見的作法是使用 RAMDisk,不過司徒想幫小志掌機使用另一個方式,這個方式是由 OpenDingux Team 的 Pcercuei 製作,GitHub Repo 在 https://github.com/pcercuei/mininit, mininit 是一個在執行 init 前,用來執行掛載 SQUASHFS 檔案的小程式,掛載後就將執行權限交給原本的 /sbin/init 程式,因此,整個執行流程如下:
      1.jpg

      要跑 mininit 前,必須要確定 Kernel 開啟如下選項

      CONFIG_DEVTMPFS_MOUNT=y
      CONFIG_CMDLINE_BOOL=y
      

      編譯 mininit

      $ cd
      $ wget https://github.com/steward-fu/website/releases/download/rg99/src_mininit.tar.gz
      $ tar xvf src_mininit.tar.gz
      $ cd mininit
      
      $ export PATH=/opt/thead/bin:$PATH
      $ vim Makefile
          CC = riscv64-unknown-linux-gnu-gcc
      
      $ make
      

      然後將開機參數改成

      bootargs = "console=ttyS4,115200n8 console=tty ignore_loglevel loglevel=8 root=/dev/mmcblk0p1 rootwait ro rootfstype=vfat init=/mininit-syspart";
      

      接著回 Buildroot 資料夾,新增一個 boot 資料夾給 mininit 掛載使用,然後再修改 etc/init.d/rcS,編譯後就可以產生我們要的 rootfs.squashfs 檔案

      $ mkdir output/target/boot
      $ sudo vim output/target/etc/init.d/rcS
          SDL_NOMOUSE=1 SDL_VIDEODRIVER=fbcon /bin/main
      
      $ sudo cp ../main output/target/bin/
      $ make
      

      接著將 rootfs.squashfs 以及編譯後的 mininit-syspart 複製到 MicroSD,同時也需要在第一磁區增加兩個空的資料夾 (dev, root),如下圖示:
      2.jpg

      開機後,就會顯示測試程式的 UI 畫面,只是這次是從 rootfs.squash 掛載執行
      3.jpg
      P.S. 測試檔案 https://github.com/steward-fu/website/releases/download/gaviar/kernel_test_20260920.img

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      接著我們來寫一個測試的 UI 應用程式試試, 程式碼如下:

      #include <stdio.h>
      #include <stdlib.h>
      #include <SDL.h>
        
      int main(int argc, char **argv)
      {
          SDL_Rect rt = { 0 };
          SDL_Surface *screen = NULL;
        
          SDL_Init(SDL_INIT_VIDEO);
          screen = SDL_SetVideoMode(320, 240, 16, SDL_SWSURFACE);
          SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0xff, 0x00, 0x00));
          SDL_ShowCursor(0);
       
          rt.x = 50;
          rt.y = 50;
          rt.w = 30;
          rt.h = 30;
          SDL_FillRect(screen, &rt, SDL_MapRGB(screen->format, 0x00, 0xff, 0x00));
        
          rt.x = 100;
          rt.y = 100;
          rt.w = 50;
          rt.h = 100;
          SDL_FillRect(screen, &rt, SDL_MapRGB(screen->format, 0x00, 0x00, 0xff));
        
          SDL_Flip(screen);
          SDL_Delay(30000);
          SDL_Quit();
       
          return 0;
      }
      

      編譯 (使用 Buildroot 編譯出來的編譯器)

      $ /opt/gaviar/bin/riscv64-linux-gcc main.c -o main -I/opt/gaviar/riscv64-Gaviar-linux-gnu/sysroot/usr/include/SDL -lSDL
      

      設定開機啟動這個 UI 應用程式, 在 SDCard 裡面的 /etc/init.d/rcS, 附加如下指令並且將編譯後的 main 複製到 SDCard 的 /bin 資料夾底下

      SDL_NOMOUSE=1 SDL_VIDEODRIVER=fbcon /bin/main
      

      開機後,就會顯示測試程式的UI畫面
      1.jpg
      P.S. 之後如果有時間移植 LVGL、Funkey-OS 或者 Onion,他們就是這個 UI 應用程式角色

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      接著我們讓小志掌機透過 Buildroot 開機到 Console 畫面試試, 步驟如下:

      $ cd
      $ wget https://buildroot.org/downloads/buildroot-2026.08.tar.gz
      $ tar xvf buildroot-2026.08.tar.gz
      $ cd buildroot-2026.08
      $ make menuconfig
      

      選擇 RISC-V
      1.jpg

      選擇 glibc、設定 kernel、選擇 C++
      2.jpg

      將編譯工具安裝到 /opt/gaviar, 稍後可以用來編譯一個 SDL 測試程式
      3.jpg

      設定 System banner (開機歡迎字樣)
      4.jpg

      選擇全部 SDL 套件
      5.jpg

      選擇 squashfs、tar
      6.jpg

      編譯

      $ make
      

      司徒將 SD 卡格式化成一個 ext4 磁區
      7.jpg

      描述開機磁區位置以及磁區類型, 還有第一個程式位置 (init=/sbin/init)

      bootargs = "console=ttyS4,115200n8 console=tty ignore_loglevel loglevel=8 root=/dev/mmcblk0p1 rootwait rw rootfstype=ext4 init=/sbin/init";
      

      將 output/images/rootfs.tar 解壓縮到第一磁區,開機後就可以看到 Welcome to Gaviar
      8.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      那問題來了,Kernel 究竟是如何載入 Buildroot File System(rootfs) 並且跑起來的呢?一般作法是將 rootfs 放到一個磁區,例如:Partition 2,然後在 Kernel 的啟動參數裡面,描述 rootfs 位於何處以及磁區的類別是什麼,例如:ext4,然後說明第一個 init 程式位於何處,所以當 Kernel 開始執行時,MMC 驅動程式就會負責偵測 SD卡,然後 FileSystem 驅動程式負責磁區的識別,最後 Kernel 會執行 init 程式,整個過程如下圖所示:
      1.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒接著說明一下移植系統 UI 這個部份,一般市面上常見的 UI 包含:LVGL、Funkey-OS、Onion 等,這些 UI 都是屬於頂層應用程式,使用的元件都是基於 Buildroot 建構出來,而 Buildroot 是一套用來建制精簡 Linux 系統的工具,因此,移植系統 UI 指的就是將這些 UI 重新編譯並且跑在我們期望的平台上,因為某些UI會針對不同晶片或者 Kernel 做客製化,因此,這部份需要先找出來並且修改成我們想要的樣子,這些層級關係,司徒整理成如下圖片
      1.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      追了下 MMC Error Log, 發現是 Regulator 問題導致,小志掌機並沒有電源管理晶片(PMU),因此,司徒使用 Dummy Regulator 替代,Device Tree 如下

      /dts-v1/;
      
      #include "sun20iw1p1.dtsi"
      
      / {
          compatible = "allwinner,d1", "arm,sun20iw1p1", "allwinner,sun20iw1p1", "allwinner,gaviar";
      
          chosen {
              bootargs = "console=ttyS4,115200n8 console=tty ignore_loglevel loglevel=8 root=/dev/mmcblk0p1 rootwait rw rootfstype=fat init=/init";
          };
      
          memory@40000000 {
              device_type = "memory";
              reg = <0x0 0x40000000 0x0 0x04000000>;
          };
      
          reserved-memory {
              #address-cells = <2>;
              #size-cells = <2>;
              ranges;
      
              opensbi_reserved: opensbi@43e00000 {
                  reg = <0x0 0x43e00000 0x0 0x00080000>;
                  no-map;
              };
      
              dtb_reserved: dtb@43d00000 {
                  reg = <0x0 0x43d00000 0x0 0x00010000>;
                  no-map;
              };
          };
      
          aliases {
              dsp0 = &dsp0;
              dsp0_gpio_int = &dsp0_gpio_int;
          };
      
          dsp0: dsp0 {
              compatible = "allwinner,sun20iw1-dsp";
              status = "okay";
          };
      
          dsp0_gpio_int: dsp0_gpio_int {
              compatible = "allwinner,sun20iw1-dsp-gpio-int";
              pin-group = "PB", "PC", "PD", "PE";
              status = "disabled";
          };
      
          reg_vdd_cpu: vdd-cpu {
              compatible = "regulator-fixed";
              regulator-name = "vdd_cpu";
              regulator-min-microvolt = <920000>;
              regulator-max-microvolt = <920000>;
              regulator-always-on;
              regulator-boot-on;
              status = "okay";
          };
      
          reg_sd_dummy: regulator-sd-dummy {
              compatible = "regulator-fixed";
              regulator-name = "sd-dummy";
              regulator-min-microvolt = <3300000>;
              regulator-max-microvolt = <3300000>;
              regulator-always-on;
          };
      };
      
      &CPU0 {
          cpu-supply = <&reg_vdd_cpu>;
      };
      
      &pio {
          sdc0_pins_a: sdc0@0 {
              allwinner,pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
              allwinner,function = "sdc0";
              allwinner,muxsel = <2>;
              allwinner,drive = <1>;
              allwinner,pull = <1>;
              pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
              function = "sdc0";
              drive-strength = <30>;
              bias-pull-up;
              power-source = <3300>;
          };
      
          uart4_pins_a: uart4_pins@0 {
              pins = "PB2", "PB3";
              function = "uart4";
              muxsel = <7>;
              drive-strength = <10>;
              bias-pull-up;
          };
      };
      
      &uart4 {
          pinctrl-names = "default";
          pinctrl-0 = <&uart4_pins_a>;
          status = "okay";
      };
      
      &sdc0 {
          pinctrl-names = "default", "mmc_1v8", "sleep";
          pinctrl-0 = <&sdc0_pins_a>;
          pinctrl-1 = <&sdc0_pins_a>;
          pinctrl-2 = <&sdc0_pins_a>;
          bus-width = <4>;
          cap-sd-highspeed;
          no-sdio;
          no-mmc;
          disable-wp;
          sunxi-power-save-mode;
          ctl-spec-caps = <0x8>;
          max-frequency = <150000000>;
          vmmc-supply = <&reg_sd_dummy>;
          vqmmc-supply = <&reg_sd_dummy>;
          vdmmc-supply = <&reg_sd_dummy>;
          vdmmc33sw-supply = <&reg_sd_dummy>;
          vqmmc33sw-supply = <&reg_sd_dummy>;
          vdmmc18sw-supply = <&reg_sd_dummy>;
          vqmmc18sw-supply = <&reg_sd_dummy>;
          status = "okay";
      };
      

      修正後的 Kernel Log 如下:

      sunxi-mmc 4020000.sdmmc: SD/MMC/SDIO Host Controller Driver(v4.25 2022-6-21 13:40)
      sunxi-mmc 4020000.sdmmc: ***ctl-spec-caps*** 8
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 0Hz bm PP pm UP vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: Switching to 3.3V signalling voltage ok
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: detmode:manually by software
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing SD-HS(SDR25) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 50000000Hz bm PP pm ON vdd 21 width 1 timing SD-HS(SDR25) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 50000000Hz bm PP pm ON vdd 21 width 4 timing SD-HS(SDR25) dt B
      mmc0: new high speed SDHC card at address aaaa
      mmcblk0: mmc0:aaaa SC16G 14.8 GiB 
       mmcblk0: p1
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      具備了按鍵和堪用的顯示後,小志掌機已經具備可以進入系統的門票,接著司徒將 MMC 驅動打開
      1.jpg

      Crash Log

      sunxi-mmc 4020000.sdmmc: SD/MMC/SDIO Host Controller Driver(v4.25 2022-6-21 13:40)
      sunxi-mmc 4020000.sdmmc: ***ctl-spec-caps*** 8
      sunxi-mmc 4020000.sdmmc: No vmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vqmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vdmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vd33sw regulator found
      sunxi-mmc 4020000.sdmmc: No vd18sw regulator found
      sunxi-mmc 4020000.sdmmc: No vq33sw regulator found
      sunxi-mmc 4020000.sdmmc: No vq18sw regulator found
      sunxi-mmc 4020000.sdmmc: Cann't get sleep pinstate,check if needed
      sunxi-mmc 4020000.sdmmc: Cann't get pin bias hs pinstate,check if needed
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 0Hz bm PP pm UP vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: no vqmmc,Check if there is regulator
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: detmode:manually by software
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      [perf] PMU initialization done
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing LEGACY(SDR12) dt B
      mmc0: host does not support reading read-only switch, assuming write-enable
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 400000Hz bm PP pm ON vdd 21 width 1 timing SD-HS(SDR25) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 25000000Hz bm PP pm ON vdd 21 width 1 timing SD-HS(SDR25) dt B
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 25000000Hz bm PP pm ON vdd 21 width 4 timing SD-HS(SDR25) dt B
      mmc0: new high speed SDHC card at address aaaa
      mmcblk0: mmc0:aaaa SC16G 14.8 GiB 
       mmcblk0: p1
      VFS: Cannot open root device "mmcblk0p1" or unknown-block(179,1): error -19
      Please append a correct "root=" boot option; here are the available partitions:
      0100            4096 ram0 
       (driver?)
      0101            4096 ram1 
       (driver?)
      0102            4096 ram2 
       (driver?)
      0103            4096 ram3 
       (driver?)
      0104            4096 ram4 
       (driver?)
      0105            4096 ram5 
       (driver?)
      0106            4096 ram6 
       (driver?)
      0107            4096 ram7 
       (driver?)
      0108            4096 ram8 
       (driver?)
      0109            4096 ram9 
       (driver?)
      010a            4096 ram10 
       (driver?)
      010b            4096 ram11 
       (driver?)
      010c            4096 ram12 
       (driver?)
      010d            4096 ram13 
       (driver?)
      010e            4096 ram14 
       (driver?)
      010f            4096 ram15 
       (driver?)
      b300        15558144 mmcblk0 
       driver: mmcblk
        b301        15541248 mmcblk0p1 6a255dd9-01
      
      Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(179,1)
      CPU: 0 PID: 1 Comm: swapper Not tainted 5.4.61 #2
      Call Trace:
      [<ffffffff800241c4>] walk_stackframe+0x0/0x98
      [<ffffffff8002437c>] show_stack+0x2a/0x34
      [<ffffffff8028153a>] dump_stack+0x20/0x28
      [<ffffffff8002a1cc>] panic+0xec/0x28a
      [<ffffffff80000ef6>] 0xffffffff80000ef6
      [<ffffffff800010de>] 0xffffffff800010de
      [<ffffffff8000125c>] 0xffffffff8000125c
      [<ffffffff80000b58>] 0xffffffff80000b58
      [<ffffffff80291c60>] kernel_init+0x12/0xee
      [<ffffffff80022f30>] ret_from_exception+0x0/0xc
      ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(179,1) ]---
      

      Crash 是正常的,因為我們還沒有準備任何 RootFS 讓 Linux Kernel 使用,不過修復 OpenSBI Timer 問題後,SD 卡已經可以正常運作,這是一個相當好的進展,畢竟我們不用花太多時間去找尋為何 SD 卡沒有抓到,而如果要更好的讓 Linux 運作,MMC 的這些 Error Log 我們應該適當去了解一下,看看有無機會修掉,不過至少 SD 卡可以正常運作了~灑花~

      mmc0: new high speed SDHC card at address aaaa
      mmcblk0: mmc0:aaaa SC16G 14.8 GiB 
       mmcblk0: p1
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      小志掌機的 Keypad 驅動程式只負責註冊 InputDevice,接著設定一個 Timer 做 GPIO 輪詢的操作,輪詢的間隔是 30ms,比較好的作法其實是中斷+輪詢,不過,簡單製作的話,輪詢方式其實已經很足夠使用了,值得一提是,GPIO 驅動程式,因為 Tina-Linux 已經移植完成,所以我們可以直接透過 GPIO 驅動程式來取得按鍵的狀態,假如沒有 GPIO 驅動程式的支援,我們就需要改用 I/O Mapping,然後自己去讀取暫存器(Register)的值做處理,兩者都可以達到目的,而處理完的結果,Keypad 驅動程式會透過 input_report_key() 回報給 Input Core 驅動程式,這樣上層的應用程式就可以透過讀取 /dev/input/event0 來取得我們回報的按鍵狀態,至於按鍵的回報,司徒採用以前丁果 A320 的按鍵映射方式,這些按鍵的選擇倒是沒有硬性規定,不過,根據司徒的觀察,以前這些高手的選擇都是經過細心思考且基於模擬器的相容問題來設計,像是 DOSBox 模擬器,加入 Enter、Escape 的按鍵映射,可以更加方便的操作
      6.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      小志掌機的Keypad驅動程式,其運作流程如下
      5.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      drivers/input/keyboard/gaviar-keypad.c

      /*
       * This program is free software; you can redistribute it and/or modify
       * it under the terms of the GNU General Public License as published by
       * the Free Software Foundation; either version 2 of the License, or
       * (at your option)any later version.
       *
       * This program is distributed in the hope that it will be useful,
       * but WITHOUT ANY WARRANTY; without even the implied warranty of
       * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       * GNU General Public License for more details.
       *
       * You should have received a copy of the GNU General Public License
       * along with this program; if not, write to the Free Software
       * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       */
       
      #include <linux/fs.h>
      #include <linux/kobject.h>
      #include <linux/cdev.h>
      #include <linux/init.h>
      #include <linux/device.h>
      #include <linux/module.h>
      #include <linux/delay.h>
      #include <linux/timer.h>
      #include <linux/gpio.h>
      #include <linux/input.h>
      #include <linux/kernel.h>
      #include <linux/slab.h>
      #include <linux/uaccess.h>
      #include <linux/backlight.h>
       
      #include <sound/core.h>
      #include <sound/pcm.h>
      #include <sound/pcm_params.h>
      #include <sound/soc.h>
      #include <sound/tlv.h>
      #include <sound/initval.h>
      #include <sound/dmaengine_pcm.h>
      #include <linux/gpio.h>
       
      #include <asm/irq.h>
       
      static int myperiod = 30;
      static struct input_dev *mydev;
      static struct timer_list mytimer;
       
      static uint32_t R_UP     = 0x0001;
      static uint32_t R_DOWN   = 0x0002;
      static uint32_t R_LEFT   = 0x0004;
      static uint32_t R_RIGHT  = 0x0008;
      static uint32_t R_A      = 0x0010;
      static uint32_t R_B      = 0x0020;
      static uint32_t R_X      = 0x0040;
      static uint32_t R_Y      = 0x0080;
      static uint32_t R_SELECT = 0x0100;
      static uint32_t R_START  = 0x0200;
      static uint32_t R_L1     = 0x0400;
      static uint32_t R_R1     = 0x0800;
      static uint32_t R_MENU   = 0x1000;
       
      static uint32_t I_UP     = 0;
      static uint32_t I_DOWN   = 0;
      static uint32_t I_LEFT   = 0;
      static uint32_t I_RIGHT  = 0;
      static uint32_t I_A      = 0;
      static uint32_t I_B      = 0;
      static uint32_t I_X      = 0;
      static uint32_t I_Y      = 0;
      static uint32_t I_SELECT = 0;
      static uint32_t I_START  = 0;
      static uint32_t I_L1     = 0;
      static uint32_t I_R1     = 0;
      static uint32_t I_MENU   = 0;
       
      static int do_input_request(uint32_t pin, const char *name)
      {
          if (gpio_request(pin, name) < 0) {
              printk("failed to request gpio: %s\n", name);
              return -1;
          }
          gpio_direction_input(pin);
       
          return 0;
      }
       
      static void print_key(uint32_t val, uint8_t is_pressed)
      {
          uint32_t i = 0;
          uint32_t map_val[] = {
              R_UP, R_DOWN, R_LEFT, R_RIGHT,
              R_A, R_B, R_X, R_Y,
              R_SELECT, R_START, R_L1, R_R1, R_MENU, -1
          };
          char *map_key[] = {
              "UP", "DOWN", "LEFT", "RIGHT",
              "A", "B", "X", "Y",
              "SELECT", "START", "L1", "R1", "MENU", ""
          };
       
          for (i = 0; map_val[i] != -1; i++) {
              if (map_val[i] == val) {
                  printk("%s: %s\n", map_key[i], is_pressed ? "DOWN" : "UP");
                  break;
              }
          }
      }
       
      static void report_key(uint32_t btn, uint32_t mask, uint8_t key)
      {
          static uint32_t btn_pressed = 0;
          static uint32_t btn_released = 0xffff;
       
          if(btn & mask) {
              btn_released &= ~mask;
              if((btn_pressed & mask) == 0) {
                  btn_pressed |= mask;
                  input_report_key(mydev, key, 1);
                  print_key(mask, 1);
              }
          }
          else {
              btn_pressed &= ~mask;
              if((btn_released & mask) == 0) {
                  btn_released |= mask;
                  input_report_key(mydev, key, 0);
                  print_key(mask, 0);
              }
          }
      }
       
      static void keypad_handler(struct timer_list *timer)
      {
          uint32_t val = 0;
          static uint32_t pre = 0;
       
          if (gpio_get_value(I_UP) == 0)     val |= R_UP;
          if (gpio_get_value(I_DOWN) == 0)   val |= R_DOWN;
          if (gpio_get_value(I_LEFT) == 0)   val |= R_LEFT;
          if (gpio_get_value(I_RIGHT) == 0)  val |= R_RIGHT;
          if (gpio_get_value(I_A) == 0)      val |= R_A;
          if (gpio_get_value(I_B) == 0)      val |= R_B;
          if (gpio_get_value(I_X) == 0)      val |= R_X;
          if (gpio_get_value(I_Y) == 0)      val |= R_Y;
          if (gpio_get_value(I_L1) == 0)     val |= R_L1;
          if (gpio_get_value(I_R1) == 0)     val |= R_R1;
          if (gpio_get_value(I_SELECT) == 0) val |= R_SELECT;
          if (gpio_get_value(I_START) == 0)  val |= R_START;
          if (gpio_get_value(I_MENU) == 0)   val |= R_MENU;
       
          if (pre != val) {
              pre = val;
              report_key(pre, R_UP,     KEY_UP);
              report_key(pre, R_DOWN,   KEY_DOWN);
              report_key(pre, R_LEFT,   KEY_LEFT);
              report_key(pre, R_RIGHT,  KEY_RIGHT);
              report_key(pre, R_A,      KEY_LEFTCTRL);
              report_key(pre, R_B,      KEY_LEFTALT);
              report_key(pre, R_X,      KEY_SPACE);
              report_key(pre, R_Y,      KEY_LEFTSHIFT);
              report_key(pre, R_L1,     KEY_TAB);
              report_key(pre, R_R1,     KEY_BACKSPACE);
              report_key(pre, R_SELECT, KEY_ESC);
              report_key(pre, R_START,  KEY_ENTER);
              report_key(pre, R_MENU,   KEY_RIGHTCTRL);
              input_sync(mydev);
          }
          mod_timer(&mytimer, jiffies + msecs_to_jiffies(myperiod));
      }
       
      static int __init kbd_init(void)
      {
          uint32_t ret = 0;
       
          I_UP     = ((32 * 4) + 0);
          I_DOWN   = ((32 * 4) + 7);
          I_LEFT   = ((32 * 4) + 6);
          I_RIGHT  = ((32 * 4) + 4);
          I_A      = ((32 * 4) + 13);
          I_B      = ((32 * 4) + 12);
          I_X      = ((32 * 4) + 11);
          I_Y      = ((32 * 4) + 2);
          I_SELECT = ((32 * 4) + 8);
          I_START  = ((32 * 4) + 9);
          I_R1     = ((32 * 4) + 10);
          I_L1     = ((32 * 4) + 1);
          I_MENU   = ((32 * 4) + 3);
       
          do_input_request(I_MENU,   "menu");
          do_input_request(I_UP,     "up");
          do_input_request(I_DOWN,   "down");
          do_input_request(I_LEFT,   "left");
          do_input_request(I_RIGHT,  "right");
          do_input_request(I_A,      "a");
          do_input_request(I_B,      "b");
          do_input_request(I_X,      "x");
          do_input_request(I_Y,      "y");
          do_input_request(I_SELECT, "select");
          do_input_request(I_START,  "start");
          do_input_request(I_L1,     "l1");
          do_input_request(I_R1,     "r1");
       
          mydev = input_allocate_device();
          set_bit(EV_KEY,         mydev-> evbit);
          set_bit(KEY_UP,         mydev->keybit);
          set_bit(KEY_DOWN,       mydev->keybit);
          set_bit(KEY_LEFT,       mydev->keybit);
          set_bit(KEY_RIGHT,      mydev->keybit);
          set_bit(KEY_LEFTCTRL,   mydev->keybit);
          set_bit(KEY_LEFTALT,    mydev->keybit);
          set_bit(KEY_SPACE,      mydev->keybit);
          set_bit(KEY_LEFTSHIFT,  mydev->keybit);
          set_bit(KEY_ENTER,      mydev->keybit);
          set_bit(KEY_ESC,        mydev->keybit);
          set_bit(KEY_TAB,        mydev->keybit);
          set_bit(KEY_BACKSPACE,  mydev->keybit);
          set_bit(KEY_RIGHTCTRL,  mydev->keybit);
          mydev->name = "gaviar-keypad";
          mydev->id.bustype = BUS_HOST;
          ret = input_register_device(mydev);
       
          timer_setup(&mytimer, keypad_handler, 0);
          mod_timer(&mytimer, jiffies + msecs_to_jiffies(myperiod));
       
          return 0;
      }
       
      static void __exit kbd_exit(void)
      {
          input_unregister_device(mydev);
          del_timer(&mytimer);
      }
       
      module_init(kbd_init);
      module_exit(kbd_exit);
       
      MODULE_LICENSE("GPL");
      MODULE_AUTHOR("Steward Fu <steward.fu@gmail.com>");
      MODULE_DESCRIPTION("keypad driver for Gaviar handheld");
      

      drivers/input/keyboard/Kconfig

      config KEYBOARD_GAVIAR
          tristate "keypad driver for Gaviar handheld"
          help
            Say Y here if you want to use keypad for Gaviar handheld.
      

      drivers/input/keyboard/Makefile

      obj-$(CONFIG_KEYBOARD_GAVIAR) += gaviar-keypad.o
      

      menuconfig 選擇 Gaviar Keypad
      1.jpg

      完成
      2.jpg
      P.S. commit ec03a6057,測試檔案 https://github.com/steward-fu/website/releases/download/gaviar/kernel_test_20260919.img

      另外,司徒都是使用 drawio 製作圖片,如果有需要,可以從這邊下載:https://github.com/steward-fu/gaviar/tree/main/drawio

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      小志掌機的按鍵電路圖
      1.jpg

      對應的按鍵如下:

      PE0  => UP
      PE7  => DOWN
      PE6  => LEFT
      PE4  => RIGHT
      PE13 => A
      PE12 => B
      PE11 => X
      PE2  => Y
      PE8  => SELECT
      PE9  => START
      PE10 => R1
      PE1  => L1
      PE3  => MENU
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒接著來說明一下如何做 Input 驅動程式的移植,Input 驅動程式支援很多種類型的裝置,包含:鍵盤、滑鼠、觸控等,司徒幫小志掌機設定的類型是鍵盤,因為是比較單純的裝置,相當適合我們的小志掌機,之後要移植的模擬器就只要處理鍵盤訊息即可,這樣的架構就很單純,所以我們要實作的部份如下紅色部份
      1.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      最後一版OpenSCAD

      $fn  = 50;
       
      module frame(off) {
          hull() {
              c = 3;
              h = 7;
       
              // left-bottom
              translate([0 - off, 4 - off / 2, 0]) cylinder(h, c, c);
              translate([6.5 - off / 2, 0 - off, 0]) cylinder(h, c, c);
       
              // right-bottom
              translate([112.5 + off / 2, 0 - off, 0]) cylinder(h, c, c);
              translate([118.5 + off, 4 - off / 2, 0]) cylinder(h, c, c);
       
              // left-top
              translate([6.5 - off / 2, 57 + off, 0]) cylinder(h, c, c);
              translate([0 - off, 53.5 + off / 2, 0]) cylinder(h, c, c);
       
              // right-top
              translate([118.5 + off, 53.5 + off / 2, 0]) cylinder(h, c, c);
              translate([112.5 + off / 2, 57 + off, 0]) cylinder(h, c, c);
          }
      }
       
      module earphone() {
          rotate([180, 0, 90]) difference() {
              translate([0, 0, 0]) cube([16, 22, 5]);
              translate([2.3, 2, 1]) cube([13, 18, 10]);
          }
      }
       
      module typec() {
          rotate([180, 0, 90]) difference() {
              translate([0, 0, 0]) cube([16.5, 13.5, 5]);
              translate([1.8, 1, 1]) cube([13.5, 11.5, 10]);
          }
      }
       
      module btn() {
          rotate([0, 180, 0]) difference() {
              translate([0, 0, 0]) cube([6, 16.5, 5]);
              translate([0, 6, 3.5]) cube([6, 6, 5]);
          }
      }
       
      module aux() {
          x = -1.6;
          y = 53;
          h = 21.6 + 0.8;
          w = 120.1;
       
          difference() {
              hull() {
                  translate([x, y, 0.1]) cylinder(8.5, 3, 3);
                  translate([w, y, 0.1]) cylinder(8.5, 3, 3);
                  translate([x, y + h, 0.1]) cylinder(8.5, 3, 3);
                  translate([w, y + h, 0.1]) cylinder(8.5, 3, 3);
              }
       
              translate([0, 0, -1]) hull() {
                  off = 1.6;
                  translate([x + off, y + off, -off]) cylinder(8.5, 3, 3);
                  translate([w - off, y + off, -off]) cylinder(8.5, 3, 3);
                  translate([x + off, y + h - off, -off]) cylinder(8.5, 3, 3);
                  translate([w - off, y + h - off, -off]) cylinder(8.5, 3, 3);
              }
       
              translate([-5, 45, -1]) cube([130, 9, 10]);
              translate([3, 50, -1]) cube([110, 10, 10]);
       
              hull() {
                  c = 3;
                  h = 7;
                  off = 0;
       
                  translate([6.5 - off / 2, 57 + off, 0]) cylinder(h, c, c);
                  translate([0 - off, 53.5 + off / 2, 0]) cylinder(h, c, c);
                  translate([118.5 + off, 53.5 + off / 2, 0]) cylinder(h, c, c);
                  translate([112.5 + off / 2, 57 + off, 0]) cylinder(h, c, c);
              }
          }
      }
       
      module back(h, off, extra) {
          c0 = 3;
          c1 = 3;
       
          translate([118.5, 0, 0]) rotate([0, 180, 0]) difference() {
              hull() {
                  translate([0 - off, 4 - off / 2, 0]) cylinder(h, c0, c0);
                  translate([6 - off / 2, 0 - off, 0]) cylinder(h, c0, c0);
                  translate([112 + off / 2, 0 - off, 0]) cylinder(h, c0, c0);
                  translate([118.5 + off, 4 - off / 2, 0]) cylinder(h, c0, c0);
                  translate([0 - off, (74.4 + off / 2) + extra, 0]) cylinder(h, c0, c0);
                  translate([118.5 + off, (74.4 + off / 2) + extra, 0]) cylinder(h, c0, c0);
       
                  translate([0 - off, 4 - off / 2, 0]) sphere(c1);
                  translate([6 - off / 2, 0 - off, 0]) sphere(c1);
                  translate([112 + off / 2, 0 - off, 0]) sphere(c1);
                  translate([118.5 + off, 4 - off / 2, 0]) sphere(c1);
                  translate([0 - off, (74.4 + off / 2) + extra, 0]) sphere(c1);
                  translate([118.5 + off, (74.4 + off / 2) + extra, 0]) sphere(c1);
              }
          }
      }
       
      module case(ch, cw, ce) {
          difference() {
              h = 7;
       
              union() {
                  holder_z = 1;
                  holder_h = h - holder_z;
       
                  difference() {
                      union() {
                          difference() {
                              frame(1.6);
                              frame(0);
                          }
       
                          translate([0, 0, 11]) {
                              back(ch, cw, ce);
                          }
                           
                          // keyboard holder
                          translate([8, 60.8, -0.3]) hull() {
                              translate([0, 0, 0]) sphere(0.8);
                              translate([101, 0, 0]) sphere(0.8);
                          }
                      }
                      translate([8, 59, -0.3]) cube([4, 2, 5]);
                      translate([12, 58.5, -0.3]) cube([88, 2, 5]);
                      translate([98, 59, -0.3]) cube([5, 2, 5]);
       
                      // sdcard
                      translate([63.5, -6, 0]) cube([12, 5, 4]);
       
                      // wires
                      translate([11, 58, 5]) cube([3, 6, 2]);
                      translate([21, 58, 5]) cube([3, 6, 2]);
                      translate([31, 58, 5]) cube([3, 6, 2]);
                      translate([60, 58, 5]) cube([3, 6, 2]);
                      translate([89, 58, 5]) cube([3, 6, 2]);
                      translate([92, 58, 5]) cube([3, 6, 2]);
                      translate([108, 58, 5]) cube([3, 6, 2]);
                  }
                   
                  // holder
                  translate([-3, 23, holder_z]) cube([2, 11, holder_h]);
                  translate([16.5, -3, holder_z]) cube([23, 1.5, holder_h]);
                  translate([89, -3, holder_z]) cube([12, 1.5, holder_h]);
       
                  difference() {
                      translate([0, 0, h - 0.1]) frame(1.6);
                      translate([-10, -10, h + 1.6]) cube([200, 200, 10]);
       
                      // wires - battery
                      translate([80, 54, -2]) cube([26, 10, 10]);
                      translate([11, 54, -2]) cube([30, 10, 10]);
                  }
       
                  difference() {
                      union() {
                          aux();
                          translate([11, 61, 8]) btn();
                          translate([21, 61, 8]) btn();
                          translate([31, 61, 8]) btn();
                          translate([38, 61.5, 8]) earphone();
                          translate([95, 61.5, 8]) typec();
                          // speaker
                          translate([66, 61, 3]) cube([1, 17, 3]);
                          // switch
                          translate([108, 65, 3]) cube([5, 2.5, 3]);
                          translate([112.5, 65, 3]) cube([2, 3.5, 3]);
                          translate([112.5, 76, 3]) cube([2, 2, 3]);
                      }
       
                      // earphone
                      translate([50, 63.8, -4.1]) cube([10, 10, 10]);
                      translate([42, 75, -5]) cube([6, 6, 10]);
       
                      // typec
                      translate([90, 63.3, -4.1]) cube([10, 5, 10]);
                      translate([96.75, 75, -5]) cube([10, 6, 10]);
                  }
              }
       
              // battery
              translate([15, 3, 0]) cube([82, 51, 12.8]);
              translate([2, 9, 0]) cube([15, 25, 10]);
       
              // screw
              c0 = 1;
              h0 = 50;
              z0 = -20;
              translate([3.5, 2, z0]) cylinder(h0, c0, c0);
              translate([46.5, 0.5, z0]) cylinder(h0, c0, c0);
              translate([109.5, 1, z0]) cylinder(h0, c0, c0);
              //translate([1, 37.3, z0]) cylinder(h0, c0, c0);
              //translate([115, 23, z0]) cylinder(h0, c0, c0);
              translate([1, 50, z0]) cylinder(h0, c0, c0);
              translate([47, 57.5, z0]) cylinder(h0, c0, c0);
              translate([109.8, 56.7, z0]) cylinder(h0, c0, c0);
       
              // screw for aux
              //translate([0.5, 64, z0]) cylinder(h0, c0, c0);
              translate([35, 64, z0]) cylinder(h0, c0, c0);
              //translate([63, 64, z0]) cylinder(h0, c0, c0);
              //translate([88, 64, z0]) cylinder(h0, c0, c0);
              //translate([118, 64, z0]) cylinder(h0, c0, c0);
              translate([0.5, 74, z0]) cylinder(h0, c0, c0);
              //translate([35, 74, z0]) cylinder(h0, c0, c0);
              translate([63, 74, z0]) cylinder(h0, c0, c0);
              translate([92, 74, z0]) cylinder(h0, c0, c0);
              translate([118, 74, z0]) cylinder(h0, c0, c0);
       
              // screw
              c1 = 1.8;
              h1 = 20;
              z1 = 11;
              translate([3.5, 2, z1]) cylinder(h1, c1, c1);
              translate([46.5, 0.5, z1]) cylinder(h1, c1, c1);
              translate([109.5, 1, z1]) cylinder(h1, c1, c1);
              //translate([1, 37.3, z1]) cylinder(h1, c1, c1);
              //translate([115, 23, z1]) cylinder(h1, c1, c1);
              translate([1, 50, z1]) cylinder(h1, c1, c1);
              translate([47, 57.5, z1]) cylinder(h1, c1, c1);
              translate([109.8, 56.7, z1]) cylinder(h1, c1, c1);
       
              // screw for aux
              //translate([0.5, 64, z1]) cylinder(h1, c1, c1);
              translate([35, 64, z1]) cylinder(h1, c1, c1);
              //translate([63, 64, z1]) cylinder(h1, c1, c1);
              //translate([88, 64, z1]) cylinder(h1, c1, c1);
              //translate([118, 64, z1]) cylinder(h1, c1, c1);
              translate([0.5, 74, z1]) cylinder(h1, c1, c1);
              //translate([35, 74, z1]) cylinder(h1, c1, c1);
              translate([63, 74, z1]) cylinder(h1, c1, c1);
              translate([92, 74, z1]) cylinder(h1, c1, c1);
              translate([118, 74, z1]) cylinder(h1, c1, c1);
          }
      }
       
      module keyboard_holder() {
          difference() {
              case(3, 1, 0);
              translate([-10, -10, 6.9]) cube([200, 200, 50]);
       
              // screw
              translate([0.5, 64, -2]) cylinder(10, 0.5, 0.5);
              translate([35, 64, -2]) cylinder(10, 0.5, 0.5);
              translate([63, 64, -2]) cylinder(10, 0.5, 0.5);
              translate([92, 64, -2]) cylinder(10, 0.5, 0.5);
              translate([118, 64, -2]) cylinder(10, 0.5, 0.5);
              translate([0.5, 74, -2]) cylinder(10, 0.5, 0.5);
              translate([35, 74, -2]) cylinder(10, 0.5, 0.5);
              translate([63, 74, -2]) cylinder(10, 0.5, 0.5);
              translate([92, 74, -2]) cylinder(10, 0.5, 0.5);
              translate([118, 74, -2]) cylinder(10, 0.5, 0.5);
          }
      }
       
      module battery_cover() {
          difference() {
              case(5, 2.4, 0.6);
              translate([0, 0, -3]) {
                  difference() {
                      translate([0, 0, 11]) {
                          difference() {
                              translate([0, 0, 0]) {
                                  back(10, 1.65, 0.2);
                              }
                              translate([-10, -10, 0]) {
                                  cube([200, 200, 10]);
                              }
                          }
                      }
                  }
              }
       
              translate([-10, -10, -12]) cube([200, 200, 16.9]);
       
              // screw
              z0 = -1;
              translate([0.5, 64, z0]) cylinder(10, 0.5, 0.5);
              translate([35, 64, z0]) cylinder(10, 0.5, 0.5);
              translate([63, 64, z0]) cylinder(10, 0.5, 0.5);
              translate([92, 64, z0]) cylinder(10, 0.5, 0.5);
              translate([118, 64, z0]) cylinder(10, 0.5, 0.5);
              translate([0.5, 74, z0]) cylinder(10, 0.5, 0.5);
              translate([35, 74, z0]) cylinder(10, 0.5, 0.5);
              translate([63, 74, z0]) cylinder(10, 0.5, 0.5);
              translate([92, 74, z0]) cylinder(10, 0.5, 0.5);
              translate([118, 74, z0]) cylinder(10, 0.5, 0.5);
       
              // earphone
              translate([40, 75, -3]) cube([10, 6, 10]);
       
              // typec
              translate([94, 75, -3]) cube([15.5, 6, 10]);
          }
      }
       
      module aux_shell() {
          ch = 3;
          cw = 1;
          ce = 0;
       
          difference() {
              h = 7;
       
              translate([-4, 50, 3]) cube([126, 27, 1.5]);
       
              union() {
                  holder_z = 1;
                  holder_h = h - holder_z;
       
                  difference() {
                      union() {
                          difference() {
                              frame(1.6);
                              frame(0);
                          }
                      }
                  }
                   
                  difference() {
                      translate([0, 0, h - 0.1]) frame(1.6);
                      translate([-10, -10, h + 1.6]) cube([200, 200, 10]);
                  }
       
                  aux();
              }
       
              // btn
              translate([4.8, 66.85 - 0.25, 2]) cube([6.5, 6.5, 5]);
              translate([4.8 + 10, 66.85 - 0.25, 2]) cube([6.5, 6.5, 5]);
              translate([4.8 + 20, 66.85 - 0.25, 2]) cube([6.5, 6.5, 5]);
       
              // earphone
              translate([8 + 34, 61.5 + 2.5, 3]) cube([6.5, 20, 5]);
              translate([8 + 34, 61.5, 3 + 1]) cube([6.5, 20, 5]);
       
              // speaker
              translate([79, 69.5, 1]) resize([20, 10, 5]) cylinder(5, 5, 5);
       
              // typec
              translate([96.75, 68, 4.2]) cube([17, 10, 5]);
              translate([109, 70, 3]) cube([3, 5, 5]);
          }
       
          difference() {
              union() {
                  z0 = 3;
                  h0 = 6;
                  translate([33, 64 - 1.75, z0]) cube([4, 8, h0]);
                  translate([-1.5, 68.5, z0]) cube([4, 7.5, h0 - 0.2]);
                  translate([61, 69, z0]) cube([4, 7.5, h0]);
                  translate([91, 71, z0]) cube([3, 5.5, h0]);
                  translate([116, 68.7, z0]) cube([4, 7.5, h0 - 0.2]);
       
                  translate([119.5, 58.5, z0]) cube([2, 15, h0 - 0.2]);
                  translate([-3, 58.5, z0]) cube([2, 15, h0 - 0.2]);
              }
       
              // screw
              z1 = 2;
              translate([35, 64, z1]) cylinder(10, 0.5, 0.5);
              translate([0.5, 74, z1]) cylinder(10, 0.5, 0.5);
              translate([63, 74, z1]) cylinder(10, 0.5, 0.5);
              translate([92, 74, z1]) cylinder(10, 0.5, 0.5);
              translate([118, 74, z1]) cylinder(10, 0.5, 0.5);
          }
      }
       
      keyboard_holder();
      battery_cover();
      aux_shell();
      

      33.jpg

      完美~
      34.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      正面
      9.jpg

      耳機座需要小改一下,用來偵測耳機插入的狀態
      11.jpg

      耳機腳位
      25.jpg

      XT897耳機腳位
      12.jpg

      蓋上背蓋
      13.jpg

      兩顆電池
      14.jpg

      背蓋使用 M1.6x10 螺絲
      15.jpg

      耳機座末端使用插入偵測墊片
      16.jpg

      改上前蓋面板
      18.jpg

      完成
      19.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      來點輕鬆一點的改造分享,這是司徒最愛的 XT897 改造,主要改造的原因是按鍵爛掉了,然後喇叭是後置,那聲音真是有夠小,基於這些原因,司徒必須好好改造一下這小寶貝~

      OpenSCAD

      $fn  = 50;
       
      module frame(off) {
          hull() {
              c = 3;
              h = 7;
       
              // left-bottom
              translate([0 - off, 4 - off / 2, 0]) cylinder(h, c, c);
              translate([6.5 - off / 2, 0 - off, 0]) cylinder(h, c, c);
       
              // right-bottom
              translate([112.5 + off / 2, 0 - off, 0]) cylinder(h, c, c);
              translate([118.5 + off, 4 - off / 2, 0]) cylinder(h, c, c);
       
              // left-top
              translate([6.5 - off / 2, 57 + off, 0]) cylinder(h, c, c);
              translate([0 - off, 53.5 + off / 2, 0]) cylinder(h, c, c);
       
              // right-top
              translate([118.5 + off, 53.5 + off / 2, 0]) cylinder(h, c, c);
              translate([112.5 + off / 2, 57 + off, 0]) cylinder(h, c, c);
          }
      }
       
      module earphone() {
          rotate([180, 0, 90]) difference() {
              translate([0, 0, 0]) cube([16, 22, 5]);
              translate([2.3, 2, 1]) cube([13, 18, 10]);
          }
      }
       
      module typec() {
          rotate([180, 0, 90]) difference() {
              translate([0, 0, 0]) cube([16.5, 13.5, 5]);
              translate([1.8, 1, 1]) cube([13.5, 11.5, 10]);
          }
      }
       
      module btn() {
          rotate([0, 180, 0]) difference() {
              translate([0, 0, 0]) cube([6, 16.5, 5]);
              translate([0, 6, 1]) cube([6, 6, 5]);
          }
      }
       
      module speaker() {
          difference() {
              translate([0, 0, 4]) cylinder(5, 8.5, 8.5);
              translate([0, 0, 3]) cylinder(5, 7.65, 7.65);
          }
      }
       
      module aux() {
          x = -1.6;
          y = 53;
          h = 21.6 + 0.8;
          w = 120.1;
       
          difference() {
              hull() {
                  translate([x, y, 0.1]) cylinder(8.5, 3, 3);
                  translate([w, y, 0.1]) cylinder(8.5, 3, 3);
                  translate([x, y + h, 0.1]) cylinder(8.5, 3, 3);
                  translate([w, y + h, 0.1]) cylinder(8.5, 3, 3);
              }
       
              translate([0, 0, -1]) hull() {
                  off = 1.6;
                  translate([x + off, y + off, -off]) cylinder(8.5, 3, 3);
                  translate([w - off, y + off, -off]) cylinder(8.5, 3, 3);
                  translate([x + off, y + h - off, -off]) cylinder(8.5, 3, 3);
                  translate([w - off, y + h - off, -off]) cylinder(8.5, 3, 3);
              }
       
              translate([-5, 45, -1]) cube([130, 9, 10]);
              translate([3, 50, -1]) cube([110, 10, 10]);
       
              hull() {
                  c = 3;
                  h = 7;
                  off = 0;
       
                  translate([6.5 - off / 2, 57 + off, 0]) cylinder(h, c, c);
                  translate([0 - off, 53.5 + off / 2, 0]) cylinder(h, c, c);
                  translate([118.5 + off, 53.5 + off / 2, 0]) cylinder(h, c, c);
                  translate([112.5 + off / 2, 57 + off, 0]) cylinder(h, c, c);
              }
          }
      }
       
      module back(h, off, extra) {
          c0 = 3;
          c1 = 3;
       
          translate([118.5, 0, 0]) rotate([0, 180, 0]) difference() {
              hull() {
                  translate([0 - off, 4 - off / 2, 0]) cylinder(h, c0, c0);
                  translate([6 - off / 2, 0 - off, 0]) cylinder(h, c0, c0);
                  translate([112 + off / 2, 0 - off, 0]) cylinder(h, c0, c0);
                  translate([118.5 + off, 4 - off / 2, 0]) cylinder(h, c0, c0);
                  translate([0 - off, (74.4 + off / 2) + extra, 0]) cylinder(h, c0, c0);
                  translate([118.5 + off, (74.4 + off / 2) + extra, 0]) cylinder(h, c0, c0);
       
                  translate([0 - off, 4 - off / 2, 0]) sphere(c1);
                  translate([6 - off / 2, 0 - off, 0]) sphere(c1);
                  translate([112 + off / 2, 0 - off, 0]) sphere(c1);
                  translate([118.5 + off, 4 - off / 2, 0]) sphere(c1);
                  translate([0 - off, (74.4 + off / 2) + extra, 0]) sphere(c1);
                  translate([118.5 + off, (74.4 + off / 2) + extra, 0]) sphere(c1);
              }
          }
      }
       
      module case(ch, cw, ce) {
          difference() {
              h = 7;
       
              union() {
                  holder_z = 1;
                  holder_h = h - holder_z;
       
                  difference() {
                      union() {
                          difference() {
                              frame(1.6);
                              frame(0);
                          }
       
                          translate([0, 0, 11]) {
                              back(ch, cw, ce);
                          }
                           
                          // keyboard holder
                          translate([8, 60.8, -0.3]) hull() {
                              translate([0, 0, 0]) sphere(0.8);
                              translate([101, 0, 0]) sphere(0.8);
                          }
                      }
                      translate([8, 59, -0.3]) cube([4, 2, 5]);
                      translate([12, 58.5, -0.3]) cube([88, 2, 5]);
                      translate([98, 59, -0.3]) cube([5, 2, 5]);
       
                      // sdcard
                      translate([63.5, -6, 0]) cube([12, 5, 4]);
       
                      // wires
                      translate([11, 58, 5]) cube([3, 6, 2]);
                      translate([38, 58, 5]) cube([3, 6, 2]);
                      translate([91, 58, 5]) cube([3, 6, 2]);
                      translate([103, 58, 5]) cube([3, 6, 2]);
                  }
                   
                  // holder
                  translate([-3, 23, holder_z]) cube([2, 11, holder_h]);
                  translate([16.5, -3, holder_z]) cube([23, 1.5, holder_h]);
                  translate([89, -3, holder_z]) cube([12, 1.5, holder_h]);
       
                  difference() {
                      translate([0, 0, h - 0.1]) frame(1.6);
                      translate([-10, -10, h + 1.6]) cube([200, 200, 10]);
       
                      // wires - battery
                      translate([80, 54, -2]) cube([26, 10, 10]);
                      translate([11, 54, -2]) cube([30, 10, 10]);
                  }
       
                  difference() {
                      union() {
                          aux();
                          translate([11, 61, 8]) btn();
                          translate([16, 61.5, 8]) earphone();
                          translate([75, 61.5, 8]) typec();
                          translate([100, 61, 8]) btn();
                          translate([112, 61, 8]) btn();
                          translate([127 / 2 - 2.5, 69.2, -1]) speaker();
                      }
       
                      // earphone
                      translate([20, 75, -5]) cube([6, 6, 10]);
       
                      // speaker
                      translate([45, 66.5, -4.1]) cube([10, 6, 10]);
       
                      // typec
                      translate([76.75, 75, -5]) cube([10, 6, 10]);
                  }
              }
       
              // battery
              translate([15, 3, 0]) cube([82, 51, 12.8]);
              translate([2, 9, 0]) cube([15, 25, 10]);
       
              // screw
              c0 = 1;
              h0 = 50;
              z0 = -20;
              translate([3.5, 2, z0]) cylinder(h0, c0, c0);
              translate([46.5, 0.5, z0]) cylinder(h0, c0, c0);
              translate([109.5, 1, z0]) cylinder(h0, c0, c0);
              //translate([1, 37.3, z0]) cylinder(h0, c0, c0);
              //translate([115, 23, z0]) cylinder(h0, c0, c0);
              translate([1, 50, z0]) cylinder(h0, c0, c0);
              translate([47, 57.5, z0]) cylinder(h0, c0, c0);
              translate([109.8, 56.7, z0]) cylinder(h0, c0, c0);
       
              // screw
              c1 = 1.8;
              h1 = 20;
              z1 = 11;
              translate([3.5, 2, z1]) cylinder(h1, c1, c1);
              translate([46.5, 0.5, z1]) cylinder(h1, c1, c1);
              translate([109.5, 1, z1]) cylinder(h1, c1, c1);
              //translate([1, 37.3, z1]) cylinder(h1, c1, c1);
              //translate([115, 23, z1]) cylinder(h1, c1, c1);
              translate([1, 50, z1]) cylinder(h1, c1, c1);
              translate([47, 57.5, z1]) cylinder(h1, c1, c1);
              translate([109.8, 56.7, z1]) cylinder(h1, c1, c1);
          }
      }
       
      module upper() {
          difference() {
              case(3, 1, 0);
              translate([-10, -10, 6.9]) cube([200, 200, 50]);
       
              // screw
              translate([0.5, 72, -2]) cylinder(10, 0.5, 0.5);
              translate([45, 72, -2]) cylinder(10, 0.5, 0.5);
              translate([118, 72, -2]) cylinder(10, 0.5, 0.5);
          }
      }
       
      module lower() {
          difference() {
              case(5, 2.4, 0.6);
              translate([0, 0, -3]) {
                  difference() {
                      translate([0, 0, 11]) {
                          difference() {
                              translate([0, 0, 0]) {
                                  back(10, 1.65, 0.2);
                              }
                              translate([-10, -10, 0]) {
                                  cube([200, 200, 10]);
                              }
                          }
                      }
                  }
              }
       
              translate([-10, -10, -12]) cube([200, 200, 16.9]);
       
              // screw
              translate([0.5, 72, 0]) cylinder(10, 0.5, 0.5);
              translate([45, 72, 0]) cylinder(10, 0.5, 0.5);
              translate([118, 72, 0]) cylinder(10, 0.5, 0.5);
       
              // earphone
              translate([18, 75, -3]) cube([10, 6, 10]);
       
              // typec
              translate([74, 75, -3]) cube([15.5, 6, 10]);
          }
      }
       
      module shell() {
          ch = 3;
          cw = 1;
          ce = 0;
       
          difference() {
              h = 7;
       
              translate([-4, 50, 3]) cube([126, 27, 1]);
       
              union() {
                  holder_z = 1;
                  holder_h = h - holder_z;
       
                  difference() {
                      union() {
                          difference() {
                              frame(1.6);
                              frame(0);
                          }
                      }
                  }
                   
                  difference() {
                      translate([0, 0, h - 0.1]) frame(1.6);
                      translate([-10, -10, h + 1.6]) cube([200, 200, 10]);
                  }
       
                  aux();
              }
       
              // btn
              translate([8 - 0.5, 70, 1]) cylinder(5, 2.25, 2.25);
              translate([8 + 89, 70, 1]) cylinder(5, 2.25, 2.25);
              translate([8 + 101, 70, 1]) cylinder(5, 2.25, 2.25);
       
              // speaker
              translate([8 + 53.5, 69.5, 1]) cylinder(5, 5, 5);
       
              // earphone
              translate([8 + 12, 60, 2]) cube([6.5, 20, 5]);
          }
       
          // support
          translate([0.5, 67, 3]) cylinder(5.8, 2, 2);
          translate([42, 67, 3]) cylinder(5.8, 2, 2);
          translate([72, 70, 3]) cylinder(5.8, 2, 2);
          translate([118, 68, 3]) cylinder(5.8, 2, 2);
       
          // speaker
          translate([8 + 53.5, 69.5, 3]) difference() {
              cylinder(1.5, 6, 6);
              cylinder(1.5, 5, 5);
          }
      }
       
      upper();
      lower();
      shell();
      

      1.jpg
      2.jpg
      3.jpg

      3D 列印
      4.jpg
      5.jpg
      17.jpg

      解焊
      8.jpg

      跳線
      10.jpg

      固定元件
      6.jpg

      走線
      7.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      補一張 Linux Timer 呼叫到 OpenSBI 的流程圖
      12.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒先添加 Timer 充當 DMA 刷屏使用,流程變成如下:
      1.jpg

      修改後的代碼

      /*
       * This program is free software; you can redistribute it and/or modify
       * it under the terms of the GNU General Public License as published by
       * the Free Software Foundation; either version 2 of the License, or
       * (at your option)any later version.
       *
       * This program is distributed in the hope that it will be useful,
       * but WITHOUT ANY WARRANTY; without even the implied warranty of
       * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       * GNU General Public License for more details.
       *
       * You should have received a copy of the GNU General Public License
       * along with this program; if not, write to the Free Software
       * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       */
       
      #include <linux/cdev.h>
      #include <linux/module.h>
      #include <linux/kernel.h>
      #include <linux/fb.h>
      #include <linux/dma-mapping.h>
      #include <linux/device.h>
      #include <linux/backlight.h>
      #include <linux/platform_device.h>
      #include <linux/uaccess.h>
      #include <linux/pm_runtime.h>
      #include <linux/interrupt.h>
      #include <linux/wait.h>
      #include <linux/clk.h>
      #include <linux/cpufreq.h>
      #include <linux/console.h>
      #include <linux/spinlock.h>
      #include <linux/slab.h>
      #include <linux/delay.h>
      #include <linux/lcm.h>
      #include <linux/clk-provider.h>
      #include <video/of_display_timing.h>
      #include <linux/gpio.h>
      #include <linux/omapfb.h>
      #include <linux/compiler.h>
      #include <linux/workqueue.h>
      #include <linux/sched.h>
      #include <asm/io.h>
      #include <asm/irq.h>
       
      #define DRIVER_NAME         "gaviar-fb"
      #define CCU_BASE            0x02001000
      #define GPIO_BASE           0x02000000
      #define LCDC_BASE           0x05461000
      #define PLL_CPU_CTRL_REG    0x0000
      #define PLL_PERI_CTRL_REG   0x0020
      #define PLL_VIDEO0_CTRL_REG 0x0040
      #define PLL_VIDEO1_CTRL_REG 0x0048
      #define DRAM_BGR_REG        0x080C
      #define G2D_CLK_REG         0x0630
      #define G2D_BGR_REG         0x063C
      #define HSTIMER_BGR_REG     0x073C
      #define TCONLCD_CLK_REG     0x0B60
      #define TCONLCD_BGR_REG     0x0B7C
      #define RISC_CLK_REG        0x0D00
      #define RISC_GATING_REG     0x0D04
      #define RISC_CFG_BGR_REG    0x0D0C
      #define PD_CFG0             0x0090
      #define PD_CFG1             0x0094
      #define PD_CFG2             0x0098
      #define PD_DAT              0x00a0
      #define LCD_RST             (1 << 0)
      #define LCD_WR              (1 << 18)
      #define LCD_RS              (1 << 19)
      #define LCD_RD              (1 << 20)
      #define LCD_CS              (1 << 21)
      #define LCD_BL              (1 << 22)
       
      struct myfb_par {
          struct device *dev;
          struct platform_device *pdev;
       
          void *vram_virt;
          uint32_t vram_size;
          dma_addr_t vram_phys;
          uint32_t pseudo_palette[16];
          struct fb_videomode mode;
      };
       
      struct iomm {
          uint8_t *ccu;
          uint8_t *gpio;
          uint8_t *lcdc;
      };
       
      static struct iomm myio = { 0 };
      static struct myfb_par *mypar = NULL;
      static struct fb_var_screeninfo myfb_var = { 0 };
      static struct fb_fix_screeninfo myfb_fix = {
          .id = DRIVER_NAME,
          .type = FB_TYPE_PACKED_PIXELS,
          .type_aux = 0,
          .visual = FB_VISUAL_TRUECOLOR,
          .xpanstep = 0,
          .ypanstep = 1,
          .ywrapstep = 0,
          .accel = FB_ACCEL_NONE
      };
       
      static void lcd_write(uint32_t ctl, uint32_t dat)
      {
          uint32_t r = ctl | ((dat & 0xff) << 1) | ((dat & 0xff00) << 2);
       
          writel(r, myio.gpio + PD_DAT);
          r |= LCD_WR;
          writel(r, myio.gpio + PD_DAT);
      }
       
      static void lcd_write_cmd(uint32_t cmd)
      {
          lcd_write(LCD_RD | LCD_BL | LCD_RST, cmd);
      }
       
      static void lcd_write_dat(uint32_t dat)
      {
          lcd_write(LCD_RS | LCD_RD | LCD_BL | LCD_RST, dat);
      }
       
      static void lcd_reset(void)
      {
          uint32_t r = 0;
       
          r = readl(myio.gpio + PD_DAT);
          r &= ~LCD_RST;
          writel(r, myio.gpio + PD_DAT);
          mdelay(150);
       
          r |= LCD_RST;
          writel(r, myio.gpio + PD_DAT);
          mdelay(150);
      }
       
      static int lcd_init(struct myfb_par *mypar)
      {
          writel(0x11111111, myio.gpio + PD_CFG0);
          writel(0x11111111, myio.gpio + PD_CFG1);
          writel(0x11111111, myio.gpio + PD_CFG2);
          writel(0xffffffff, myio.gpio + PD_DAT);
       
          lcd_reset();
          lcd_write_cmd(0xb2);
          lcd_write_dat(0x5c);
          lcd_write_dat(0x5c);
          lcd_write_dat(0x00);
          lcd_write_dat(0x33);
          lcd_write_dat(0x33);
       
          lcd_write_cmd(0xb7);
          lcd_write_dat(0x35);
       
          lcd_write_cmd(0x21);
       
          lcd_write_cmd(0x11);
       
          mdelay(150);
       
          lcd_write_cmd(0xe0);
          lcd_write_dat(0xd0);
          lcd_write_dat(0x06);
          lcd_write_dat(0x0b);
          lcd_write_dat(0x07);
          lcd_write_dat(0x07);
          lcd_write_dat(0x24);
          lcd_write_dat(0x2e);
          lcd_write_dat(0x32);
          lcd_write_dat(0x46);
          lcd_write_dat(0x37);
          lcd_write_dat(0x13);
          lcd_write_dat(0x13);
          lcd_write_dat(0x2d);
          lcd_write_dat(0x33);
       
          lcd_write_cmd(0xe1);
          lcd_write_dat(0xd0);
          lcd_write_dat(0x02);
          lcd_write_dat(0x06);
          lcd_write_dat(0x09);
          lcd_write_dat(0x08);
          lcd_write_dat(0x05);
          lcd_write_dat(0x29);
          lcd_write_dat(0x44);
          lcd_write_dat(0x42);
          lcd_write_dat(0x38);
          lcd_write_dat(0x14);
          lcd_write_dat(0x14);
          lcd_write_dat(0x2a);
          lcd_write_dat(0x30);
       
          lcd_write_cmd(0x36);
          lcd_write_dat(0xb0);
       
          lcd_write_cmd(0x2a);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0x01);
          lcd_write_dat(0x3f);
       
          lcd_write_cmd(0x2b);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0xef);
       
          lcd_write_cmd(0x3a);
          lcd_write_dat(0x55);
       
          lcd_write_cmd(0x29);
          lcd_write_cmd(0x2c);
       
          return 0;
      }
       
      static int myfb_setcolreg(unsigned n, unsigned r, unsigned g, unsigned b, unsigned t, struct fb_info *info)
      {
          #define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
          r = CNVT_TOHW(r, info->var.red.length);
          b = CNVT_TOHW(b, info->var.blue.length);
          g = CNVT_TOHW(g, info->var.green.length);
       
          ((u32 *)(info->pseudo_palette))[n] =
              (r << info->var.red.offset) |
              (g << info->var.green.offset) |
              (b << info->var.blue.offset);
       
          return 0;
      }
       
      static int myfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
      {
          struct myfb_par *par = info->par;
          unsigned int line_size = var->xres_virtual * 2;
       
          if ((var->xres != 320) || (var->yres != 240) || (var->bits_per_pixel != 16)) {
              return -EINVAL;
          }
       
          var->transp.offset = 0;
          var->transp.length = 0;
          var->red.offset = 11;
          var->red.length = 5;
          var->green.offset = 5;
          var->green.length = 6;
          var->blue.offset = 0;
          var->blue.length = 5;
          var->red.msb_right = 0;
          var->green.msb_right = 0;
          var->blue.msb_right = 0;
          var->transp.msb_right = 0;
       
          if (line_size * var->yres_virtual > par->vram_size) {
              var->yres_virtual = par->vram_size / line_size;
          }
          if (var->yres > var->yres_virtual) {
              var->yres = var->yres_virtual;
          }
          if (var->xres > var->xres_virtual) {
              var->xres = var->xres_virtual;
          }
          if (var->xres + var->xoffset > var->xres_virtual) {
              var->xoffset = var->xres_virtual - var->xres;
          }
          if (var->yres + var->yoffset > var->yres_virtual) {
              var->yoffset = var->yres_virtual - var->yres;
          }
       
          return 0;
      }
       
      static int myfb_set_par(struct fb_info *info)
      {
          struct myfb_par *par = info->par;
       
          fb_var_to_videomode(&par->mode, &info->var);
          info->fix.visual = FB_VISUAL_TRUECOLOR;
          info->fix.line_length = 320 * 2;
       
          return 0;
      }
       
      static int myfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
      {
          info->var.xoffset = var->xoffset;
          info->var.yoffset = var->yoffset;
       
          return 0;
      }
       
      static struct fb_ops myfb_ops = {
          .owner = THIS_MODULE,
          .fb_set_par = myfb_set_par,
          .fb_setcolreg = myfb_setcolreg,
          .fb_check_var = myfb_check_var,
          .fb_pan_display = myfb_pan_display,
       
          .fb_fillrect = sys_fillrect,
          .fb_copyarea = sys_copyarea,
          .fb_imageblit = sys_imageblit,
      };
       
      static struct timer_list my_timer;
       
      static void timer_handler(struct timer_list *timer)
      {
          static unsigned int cc = 0;
       
          if (mypar && mypar->vram_virt) {
              int i = 0;
              uint16_t *p = mypar->vram_virt;
       
              for (i = 0; i < 320 * 240; i++) {
                  lcd_write_dat(*p++);
              }
          }
       
          printk("%d\n", cc++);
          mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));
      }
       
      static int myfb_probe(struct platform_device *device)
      {
          int ret = 0;
          struct fb_info *info = NULL;
          struct myfb_par *par = NULL;
          struct fb_videomode *mode = NULL;
       
          mode = devm_kzalloc(&device->dev, sizeof(struct fb_videomode), GFP_KERNEL);
          if (mode == NULL) {
              return -ENOMEM;
          }
          mode->name = "320x240";
          mode->xres = 320;
          mode->yres = 240;
          mode->vmode = FB_VMODE_NONINTERLACED;
       
          info = framebuffer_alloc(sizeof(struct myfb_par), &device->dev);
          if (!info) {
              return -ENOMEM;
          }
       
          par = info->par;
          par->pdev = device;
          par->dev = &device->dev;
          fb_videomode_to_var(&myfb_var, mode);
       
          par->vram_size = 320 * 240 * 2 * 2;
          par->vram_virt = dma_alloc_coherent(&device->dev, 
              par->vram_size,
              (resource_size_t *)&par->vram_phys,
              GFP_KERNEL | GFP_DMA
          );
       
          if (!par->vram_virt) {
              return -EINVAL;
          }
          info->screen_base = (char __iomem *)par->vram_virt;
          myfb_fix.smem_start = par->vram_phys;
          myfb_fix.smem_len = par->vram_size;
          myfb_fix.line_length = 320 * 2;
          myfb_var.grayscale = 0;
          myfb_var.bits_per_pixel = 16;
       
          info->flags = FBINFO_FLAG_DEFAULT;
          info->fix = myfb_fix;
          info->var = myfb_var;
          info->fbops = &myfb_ops;
          info->pseudo_palette = par->pseudo_palette;
          info->fix.visual = (info->var.bits_per_pixel <= 8) ?
              FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
          ret = fb_alloc_cmap(&info->cmap, 256, 0);
          if (ret) {
              return -EINVAL;
          }
          info->cmap.len = 32;
       
          myfb_var.activate = FB_ACTIVATE_FORCE;
          fb_set_var(info, &myfb_var);
          dev_set_drvdata(&device->dev, info);
          if (register_framebuffer(info) < 0) {
              return -EINVAL;
          }
          lcd_init(par);
       
          mypar = par;
          timer_setup(&my_timer, timer_handler, 0);
          mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
       
          return 0;
      }
       
      static int myfb_remove(struct platform_device *dev)
      {
          struct fb_info *info = dev_get_drvdata(&dev->dev);
          struct myfb_par *par = info->par;
       
          if (info) {
              flush_scheduled_work();
              unregister_framebuffer(info);
              fb_dealloc_cmap(&info->cmap);
              dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys);
              framebuffer_release(info);
          }
          iounmap(myio.gpio);
       
          return 0;
      }
       
      static int myfb_suspend(struct platform_device *dev, pm_message_t state)
      {
          struct fb_info *info = platform_get_drvdata(dev);
       
          console_lock();
          fb_set_suspend(info, 1);
          console_unlock();
       
          return 0;
      }
       
      static int myfb_resume(struct platform_device *dev)
      {
          struct fb_info *info = platform_get_drvdata(dev);
       
          console_lock();
          fb_set_suspend(info, 0);
          console_unlock();
       
          return 0;
      }
       
      static const struct of_device_id fb_of_match[] = { { .compatible = "allwinner,sunxi-disp" }, {} };
      MODULE_DEVICE_TABLE(of, fb_of_match);
       
      static struct platform_driver fb_driver = {
          .probe = myfb_probe,
          .resume = myfb_resume,
          .remove = myfb_remove,
          .suspend = myfb_suspend,
          .driver = {
              .name   = DRIVER_NAME,
              .owner  = THIS_MODULE,
              .of_match_table = of_match_ptr(fb_of_match),
          },
      };
       
      static void sunxi_ioremap(void)
      {    
          myio.ccu = (uint8_t *)ioremap(CCU_BASE, 4096);
          myio.gpio = (uint8_t *)ioremap(GPIO_BASE, 4096);
          myio.lcdc = (uint8_t *)ioremap(LCDC_BASE, 4096);
       
          printk("%s\n", __func__);
       
          writel((1 << 31) | (1 << 24) | (1 << 16) | (1 << 0), myio.ccu + G2D_CLK_REG);
          writel((1 << 16) | (1 << 0), myio.ccu + G2D_BGR_REG);
       
          writel((1 << 31) | (1 << 24), myio.ccu + TCONLCD_CLK_REG);
          writel((1 << 16) | (1 << 0), myio.ccu + TCONLCD_BGR_REG);
       
          writel((1 << 16) | (1 << 0), myio.ccu + HSTIMER_BGR_REG);
      }
       
      static void sunxi_iounmap(void)
      {
          iounmap(myio.ccu);
          iounmap(myio.gpio);
          iounmap(myio.lcdc);
      }
       
      static int __init myfb_init(void)
      {
          sunxi_ioremap();
          return platform_driver_register(&fb_driver);
      }
       
      static void __exit myfb_cleanup(void)
      {
          platform_driver_unregister(&fb_driver);
          sunxi_iounmap();
      }
       
      module_init(myfb_init);
      module_exit(myfb_cleanup);
       
      MODULE_DESCRIPTION("framebuffer driver for Gaviar handheld");
      MODULE_AUTHOR("Steward Fu <steward.fu@gmail.com>");
      MODULE_LICENSE("GPL");
      

      更新 Kernel 後,Log 就會自動更新到屏幕上
      2.jpg
      P.S. 測試檔案 https://github.com/steward-fu/website/releases/download/gaviar/kernel_test_20260918.img

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒是打算先使用 Timer 做屏幕刷新,這樣就可以先移植輸入驅動程式,這才發現 Linux Kernel 啟動後,Timer 是無法正常運作的,往回找問題,才意識到當初在移植 OpenSBI 時,忘記做Timer、IPI、IRQ 初始化,因此,再度更新 OpenSBI Platform 初始化的部份,修改後的方塊圖如下
      11.jpg
      P.S. 修改的 Commit 57c01448d198b3ed53aa83951195f550e0658428

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 求大神帮忙DIY流媒体后视镜

      AI 沒有騙你, 大致流程確實是如此, 不過, AI 把一些細節隱藏起來, 如果沒錯, 我猜你第一步就會卡關~

      司徒蠻想試試你那個 AI 的能力可以到哪, 如果你願意一起來作實驗, 你把你用的 AI 模型列出來, 讓我先知道是哪個模型~

      AI 給你的第一步是 adb 進入系統, 因此, 你可以先看系統有沒有 USB 端口, 如果有, 你可以直接插入電腦, 然後看看是哪一個裝置被電腦發現, 如果沒有 USB 端口, 你可以拍照給 AI, 請 AI 給你找出 adb 的步驟~

      當然, 如果你想直接找其他人解決, 那以上就當我沒說~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 求大神帮忙DIY流媒体后视镜

      尷尬了~我竟然無法移動主題到 V系列區域, 哈, 那就是代表有緣啦~
      那你有"视镜主控为全志V535"相關的資訊嗎?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 求大神帮忙DIY流媒体后视镜

      可能要麻煩你將更細的描述添加上來, 這樣大家才知道要如何幫你, 例如:目前后视镜的資源你有哪些?以及你預計實作的項目又是哪些?還有你目前掌握的技術可以製作到哪邊?

      另外, 這個貼可以幫你移到 V Series 芯片專區嗎?因為那邊會比較專注在討論 V系列的芯片

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒畫了一張Framebuffer的流程圖,這樣可以更知道整個Framebuffer Driver的運作,至於司徒為何還是使用Framebuffer Driver而不是其它更新的Video Driver Model呢?因為太難的司徒也不會...
      3.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒看了一下目前Tina-Linux裡面的Video Framebuffer Driver,發現這個Video Driver是為了可以透過初始化參數的設定就可以點屏,架構相對來說比較複雜一點,對於初期可以很快拿來使用,但是後期我們要做最佳化時,會變成是一個瓶頸,司徒覺得不適合我們短小精幹的小志掌機,為此,司徒將小志掌機的顯示驅動拆成如下三個階段製作:

      階段一:使用I/O Toggle方式顯示
      階段二:使用DMA方式顯示
      階段三:加上INT中斷掃屏
      

      framebuffer位於drivers/video/fbdev資料夾,因此,我們創建一個新檔案st7789.c

      /*
       * This program is free software; you can redistribute it and/or modify
       * it under the terms of the GNU General Public License as published by
       * the Free Software Foundation; either version 2 of the License, or
       * (at your option)any later version.
       *
       * This program is distributed in the hope that it will be useful,
       * but WITHOUT ANY WARRANTY; without even the implied warranty of
       * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       * GNU General Public License for more details.
       *
       * You should have received a copy of the GNU General Public License
       * along with this program; if not, write to the Free Software
       * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       */
      
      #include <linux/cdev.h>
      #include <linux/module.h>
      #include <linux/kernel.h>
      #include <linux/fb.h>
      #include <linux/dma-mapping.h>
      #include <linux/device.h>
      #include <linux/backlight.h>
      #include <linux/platform_device.h>
      #include <linux/uaccess.h>
      #include <linux/pm_runtime.h>
      #include <linux/interrupt.h>
      #include <linux/wait.h>
      #include <linux/clk.h>
      #include <linux/cpufreq.h>
      #include <linux/console.h>
      #include <linux/spinlock.h>
      #include <linux/slab.h>
      #include <linux/delay.h>
      #include <linux/lcm.h>
      #include <linux/clk-provider.h>
      #include <video/of_display_timing.h>
      #include <linux/gpio.h>
      #include <linux/omapfb.h>
      #include <linux/compiler.h>
      #include <linux/workqueue.h>
      #include <linux/sched.h>
      #include <asm/io.h>
      #include <asm/irq.h>
      
      #define PALETTE_SIZE    256
      #define DRIVER_NAME     "gaviar-fb"
      #define GPIO_BASE       0x02000000
      #define PD_CFG0         0x0090
      #define PD_CFG1         0x0094
      #define PD_CFG2         0x0098
      #define PD_DAT          0x00a0
      #define LCD_RST         (1 << 0)
      #define LCD_WR          (1 << 18)
      #define LCD_RS          (1 << 19)
      #define LCD_RD          (1 << 20)
      #define LCD_CS          (1 << 21)
      #define LCD_BL          (1 << 22)
      
      struct myfb_par {
          struct device *dev;
          struct platform_device *pdev;
          void *vram_virt;
          uint32_t vram_size;
          dma_addr_t vram_phys;
          u32 pseudo_palette[16];
          struct fb_videomode mode;
      };
      
      struct iomm {
          uint8_t *gpio;
      };
      
      static struct iomm myio = { 0 };
      static struct fb_var_screeninfo myfb_var = { 0 };
      static struct fb_fix_screeninfo myfb_fix = {
          .id = DRIVER_NAME,
          .type = FB_TYPE_PACKED_PIXELS,
          .type_aux = 0,
          .visual = FB_VISUAL_TRUECOLOR,
          .xpanstep = 0,
          .ypanstep = 1,
          .ywrapstep = 0,
          .accel = FB_ACCEL_NONE
      };
      
      static void lcd_write(uint32_t ctl, uint32_t dat)
      {
          uint32_t r = ctl | ((dat & 0xff) << 1) | ((dat & 0xff00) << 2);
      
          writel(r, myio.gpio + PD_DAT);
          r |= LCD_WR;
          writel(r, myio.gpio + PD_DAT);
      }
      
      static void lcd_write_cmd(uint32_t cmd)
      {
          lcd_write(LCD_RD | LCD_BL | LCD_RST, cmd);
      }
      
      static void lcd_write_dat(uint32_t dat)
      {
          lcd_write(LCD_RS | LCD_RD | LCD_BL | LCD_RST, dat);
      }
      
      static void lcd_reset(void)
      {
          uint32_t r = 0;
      
          r = readl(myio.gpio + PD_DAT);
          r &= ~LCD_RST;
          writel(r, myio.gpio + PD_DAT);
          mdelay(150);
      
          r |= LCD_RST;
          writel(r, myio.gpio + PD_DAT);
          mdelay(150);
      }
      
      static int lcd_init(struct myfb_par *mypar)
      {
          writel(0x11111111, myio.gpio + PD_CFG0);
          writel(0x11111111, myio.gpio + PD_CFG1);
          writel(0x11111111, myio.gpio + PD_CFG2);
          writel(0xffffffff, myio.gpio + PD_DAT);
      
          lcd_reset();
          lcd_write_cmd(0xb2);
          lcd_write_dat(0x5c);
          lcd_write_dat(0x5c);
          lcd_write_dat(0x00);
          lcd_write_dat(0x33);
          lcd_write_dat(0x33);
      
          lcd_write_cmd(0xb7);
          lcd_write_dat(0x35);
      
          lcd_write_cmd(0x21);
      
          lcd_write_cmd(0x11);
      
          mdelay(150);
      
          lcd_write_cmd(0xe0);
          lcd_write_dat(0xd0);
          lcd_write_dat(0x06);
          lcd_write_dat(0x0b);
          lcd_write_dat(0x07);
          lcd_write_dat(0x07);
          lcd_write_dat(0x24);
          lcd_write_dat(0x2e);
          lcd_write_dat(0x32);
          lcd_write_dat(0x46);
          lcd_write_dat(0x37);
          lcd_write_dat(0x13);
          lcd_write_dat(0x13);
          lcd_write_dat(0x2d);
          lcd_write_dat(0x33);
      
          lcd_write_cmd(0xe1);
          lcd_write_dat(0xd0);
          lcd_write_dat(0x02);
          lcd_write_dat(0x06);
          lcd_write_dat(0x09);
          lcd_write_dat(0x08);
          lcd_write_dat(0x05);
          lcd_write_dat(0x29);
          lcd_write_dat(0x44);
          lcd_write_dat(0x42);
          lcd_write_dat(0x38);
          lcd_write_dat(0x14);
          lcd_write_dat(0x14);
          lcd_write_dat(0x2a);
          lcd_write_dat(0x30);
      
          lcd_write_cmd(0x36);
          lcd_write_dat(0xb0);
      
          lcd_write_cmd(0x2a);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0x01);
          lcd_write_dat(0x3f);
      
          lcd_write_cmd(0x2b);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0x00);
          lcd_write_dat(0xef);
      
          lcd_write_cmd(0x3a);
          lcd_write_dat(0x55);
      
          lcd_write_cmd(0x29);
          lcd_write_cmd(0x2c);
      
          {
              int i = 0;
              uint16_t *p = mypar->vram_virt;
      
              for (i = 0; i < 320 * 240; i++) {
                  lcd_write_dat(*p++);
              }
          }
      
          return 0;
      }
      
      static int myfb_setcolreg(unsigned n, unsigned r, unsigned g, unsigned b, unsigned t, struct fb_info *info)
      {
          #define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
          r = CNVT_TOHW(r, info->var.red.length);
          b = CNVT_TOHW(b, info->var.blue.length);
          g = CNVT_TOHW(g, info->var.green.length);
      
          ((u32 *)(info->pseudo_palette))[n] =
              (r << info->var.red.offset) |
              (g << info->var.green.offset) |
              (b << info->var.blue.offset);
      
          return 0;
      }
      
      static int myfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
      {
          int bpp = var->bits_per_pixel >> 3;
          struct myfb_par *par = info->par;
          unsigned long line_size = var->xres_virtual * bpp;
      
          if ((var->xres != 320) || (var->yres != 240) || (var->bits_per_pixel != 16)) {
              return -EINVAL;
          }
      
          var->transp.offset = 0;
          var->transp.length = 0;
          var->red.offset = 11;
          var->red.length = 5;
          var->green.offset = 5;
          var->green.length = 6;
          var->blue.offset = 0;
          var->blue.length = 5;
          var->red.msb_right = 0;
          var->green.msb_right = 0;
          var->blue.msb_right = 0;
          var->transp.msb_right = 0;
      
          if (line_size * var->yres_virtual > par->vram_size) {
              var->yres_virtual = par->vram_size / line_size;
          }
          if (var->yres > var->yres_virtual) {
              var->yres = var->yres_virtual;
          }
          if (var->xres > var->xres_virtual) {
              var->xres = var->xres_virtual;
          }
          if (var->xres + var->xoffset > var->xres_virtual) {
              var->xoffset = var->xres_virtual - var->xres;
          }
          if (var->yres + var->yoffset > var->yres_virtual) {
              var->yoffset = var->yres_virtual - var->yres;
          }
          return 0;
      }
      
      static int myfb_set_par(struct fb_info *info)
      {
          struct myfb_par *par = info->par;
      
          fb_var_to_videomode(&par->mode, &info->var);
          info->fix.visual = FB_VISUAL_TRUECOLOR;
          info->fix.line_length = 320 * 2;
      
          return 0;
      }
      
      static int myfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
      {
          info->var.xoffset = var->xoffset;
          info->var.yoffset = var->yoffset;
      
          return 0;
      }
      
      static struct fb_ops myfb_ops = {
          .owner          = THIS_MODULE,
          .fb_check_var   = myfb_check_var,
          .fb_set_par     = myfb_set_par,
          .fb_setcolreg   = myfb_setcolreg,
          .fb_pan_display = myfb_pan_display,
      
          .fb_fillrect  = sys_fillrect,
          .fb_copyarea  = sys_copyarea,
          .fb_imageblit = sys_imageblit,
      };
      
      static int myfb_probe(struct platform_device *device)
      {
          int ret = 0;
          struct fb_info *info = NULL;
          struct myfb_par *par = NULL;
          struct fb_videomode *mode = NULL;
      
          mode = devm_kzalloc(&device->dev, sizeof(struct fb_videomode), GFP_KERNEL);
          if (mode == NULL) {
              return -ENOMEM;
          }
          mode->name = "320x240";
          mode->xres = 320;
          mode->yres = 240;
          mode->vmode = FB_VMODE_NONINTERLACED;
          pm_runtime_enable(&device->dev);
          pm_runtime_get_sync(&device->dev);
      
          info = framebuffer_alloc(sizeof(struct myfb_par), &device->dev);
          if (!info) {
              return -ENOMEM;
          }
      
          par = info->par;
          par->pdev = device;
          par->dev = &device->dev;
          fb_videomode_to_var(&myfb_var, mode);
      
          par->vram_size = (320 * 240 * 2 * 4) + 4096;
          par->vram_virt = dma_alloc_coherent(&device->dev, par->vram_size, (resource_size_t *)&par->vram_phys, GFP_KERNEL | GFP_DMA);
          if (!par->vram_virt) {
              return -EINVAL;
          }
          info->screen_base = (char __iomem *)par->vram_virt;
          myfb_fix.smem_start = par->vram_phys;
          myfb_fix.smem_len = par->vram_size;
          myfb_fix.line_length = 320 * 2;
          myfb_var.grayscale = 0;
          myfb_var.bits_per_pixel = 16;
      
          info->flags = FBINFO_FLAG_DEFAULT;
          info->fix = myfb_fix;
          info->var = myfb_var;
          info->fbops = &myfb_ops;
          info->pseudo_palette = par->pseudo_palette;
          info->fix.visual = (info->var.bits_per_pixel <= 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
          ret = fb_alloc_cmap(&info->cmap, PALETTE_SIZE, 0);
          if (ret) {
              return -EINVAL;
          }
          info->cmap.len = 32;
      
          myfb_var.activate = FB_ACTIVATE_FORCE;
          fb_set_var(info, &myfb_var);
          dev_set_drvdata(&device->dev, info);
          if (register_framebuffer(info) < 0) {
              return -EINVAL;
          }
          lcd_init(par);
      
          return 0;
      }
      
      static int myfb_remove(struct platform_device *dev)
      {
          struct fb_info *info = dev_get_drvdata(&dev->dev);
          struct myfb_par *par = info->par;
      
          if (info) {
              flush_scheduled_work();
              unregister_framebuffer(info);
              fb_dealloc_cmap(&info->cmap);
              dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys);
              framebuffer_release(info);
          }
          iounmap(myio.gpio);
      
          return 0;
      }
      
      static int myfb_suspend(struct platform_device *dev, pm_message_t state)
      {
          struct fb_info *info = platform_get_drvdata(dev);
      
          console_lock();
          fb_set_suspend(info, 1);
          pm_runtime_put_sync(&dev->dev);
          console_unlock();
      
          return 0;
      }
      
      static int myfb_resume(struct platform_device *dev)
      {
          struct fb_info *info = platform_get_drvdata(dev);
      
          console_lock();
          pm_runtime_get_sync(&dev->dev);
          fb_set_suspend(info, 0);
          console_unlock();
      
          return 0;
      }
      
      static const struct of_device_id fb_of_match[] = { { .compatible = "allwinner,sunxi-disp" }, {} };
      MODULE_DEVICE_TABLE(of, fb_of_match);
      
      static struct platform_driver fb_driver = {
          .probe    = myfb_probe,
          .remove   = myfb_remove,
          .suspend  = myfb_suspend,
          .resume   = myfb_resume,
          .driver = {
              .name   = DRIVER_NAME,
              .owner  = THIS_MODULE,
              .of_match_table = of_match_ptr(fb_of_match),
          },
      };
      
      static void sunxi_ioremap(void)
      {    
          myio.gpio = (uint8_t *)ioremap(GPIO_BASE, 1024);
      }
      
      static void sunxi_iounmap(void)
      {
          iounmap(myio.gpio);
      }
      
      static int __init myfb_init(void)
      {
          sunxi_ioremap();
          return platform_driver_register(&fb_driver);
      }
      
      static void __exit myfb_cleanup(void)
      {
          platform_driver_unregister(&fb_driver);
          sunxi_iounmap();
      }
      
      module_init(myfb_init);
      module_exit(myfb_cleanup);
      
      MODULE_DESCRIPTION("framebuffer driver for Gaviar handheld");
      MODULE_AUTHOR("Steward Fu <steward.fu@gmail.com>");
      MODULE_LICENSE("GPL");
      

      drivers/video/fbdev/Kconfig

      config FB_ST7789
          tristate "ST7789 framebuffer support"
          depends on FB
          select FB_SYS_FOPS
          select FB_SYS_FILLRECT
          select FB_SYS_COPYAREA
          select FB_SYS_IMAGEBLIT
          ---help---
            Frame buffer driver for the Gaviar handheld
      

      drivers/video/fbdev/Makefile

      obj-$(CONFIG_FB_ST7789) += st7789.o
      

      選擇ST7789
      1.jpg

      編譯下載後就可以看到Linux Boot Logo
      2.jpg

      如果不想手動編譯,可以使用司徒做好的測試Image,如果要自己手動編譯,Commit使用44f254c025465ea58a2f024482f572f59567cb2b

      $ cd
      $ wget https://github.com/steward-fu/website/releases/download/gaviar/kernel_test_20260912.img
      $ sudo dd if=kernel_test_20260912.img of=/dev/sdX bs=1M
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      Kernel Crash 問題已經解決,但是從 Log 的結果來看,Kernel 並沒有進入到最後的階段,因為我們尚未描述 rootfs 位置,理應有 VFS Crash 的 Log 才對,所以我們還要繼續追問題

      ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
      sunxi-ehci: EHCI SUNXI driver
      get ehci0-controller wakeup-source is fail.
      sunxi ehci0-controller don't init wakeup source
      [sunxi-ehci0]: probe, pdev->name: 4101000.ehci0-controller, sunxi_ehci: 0xffffffe00051a130, 0x:ffffffd004078000, irq_no:2e
      [sunxi-ehci0]: Not init ehci0
      get ehci1-controller wakeup-source is fail.
      sunxi ehci1-controller don't init wakeup source
      [sunxi-ehci1]: probe, pdev->name: 4200000.ehci1-controller, sunxi_ehci: 0xffffffe00051a8c0, 0x:ffffffd00407c000, irq_no:31
      sunxi-ehci 4200000.ehci1-controller: 4200000.ehci1-controller supply drvvbus not found, using dummy regulator
      sunxi-ehci 4200000.ehci1-controller: 4200000.ehci1-controller supply hci not found, using dummy regulator
      sunxi-ehci 4200000.ehci1-controller: EHCI Host Controller
      sunxi-ehci 4200000.ehci1-controller: new USB bus registered, assigned bus number 1
      sunxi-ehci 4200000.ehci1-controller: irq 49, io mem 0x04200000
      

      先把 USB 的 DTS 描述進行關閉來定位問題

      &ehci0 {
          status = "disabled";
      };
      
      &ohci0 {
          status = "disabled";
      };
      

      關閉後的 Kernel Log

      ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
      sunxi-ehci: EHCI SUNXI driver
      ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
      sunxi-ohci: OHCI SUNXI driver
      usbcore: registered new interface driver uas
      usbcore: registered new interface driver usb-storage
      sunxi-rtc 7090000.rtc: errata__fix_alarm_day_reg_default_value(): ALARM0_DAY_REG=0, set it to 1
      sunxi-rtc 7090000.rtc: Warning: Using internal RC 16M clock source. Time may be inaccurate!
      sunxi-rtc 7090000.rtc: Warning: Using internal RC 16M clock source. Time may be inaccurate!
      sunxi-rtc 7090000.rtc: Warning: Using internal RC 16M clock source. Time may be inaccurate!
      sunxi-rtc 7090000.rtc: registered as rtc0
      sunxi-rtc 7090000.rtc: Warning: Using internal RC 16M clock source. Time may be inaccurate!
      sunxi-rtc 7090000.rtc: setting system clock to 1970-01-01T00:00:11 UTC (11)
      sunxi-rtc 7090000.rtc: sunxi rtc probed
      i2c /dev entries driver
      sunxi-mmc 4020000.sdmmc: SD/MMC/SDIO Host Controller Driver(v4.25 2022-6-21 13:40)
      sunxi-mmc 4020000.sdmmc: ***ctl-spec-caps*** 8
      sunxi-mmc 4020000.sdmmc: No vmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vqmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vdmmc regulator found
      sunxi-mmc 4020000.sdmmc: No vd33sw regulator found
      sunxi-mmc 4020000.sdmmc: No vd18sw regulator found
      sunxi-mmc 4020000.sdmmc: No vq33sw regulator found
      sunxi-mmc 4020000.sdmmc: No vq18sw regulator found
      sunxi-mmc 4020000.sdmmc: Got CD GPIO
      sunxi-mmc 4020000.sdmmc: set cd-gpios as 24M fail
      sunxi-mmc 4020000.sdmmc: sdc set ios:clk 0Hz bm PP pm UP vdd 21 width 1 timing LEGACY(SDR12) dt B
      sunxi-mmc 4020000.sdmmc: no vqmmc,Check if there is regulator
      

      接著在 Kernel menuconfig 也將 MMC 關閉,DTS 裡面除了 UART4 的 SDC 也關閉

      misc dump reg init
      brd: module loaded
      loop: module loaded
      i2c /dev entries driver
      ashmem: initialized
      

      但是 Kernel 還是沒有進入載入 init 的階段,因此,從 initcall 印出 Log

      $ vim init/main.c
          1012 static void __init do_initcall_level(int level)
          1013 {
          1014     int ret = 0;
          1015     initcall_entry_t *fn;
          1016 
          1017     strcpy(initcall_command_line, saved_command_line);
          1018     parse_args(initcall_level_names[level],
          1019            initcall_command_line, __start___param,
          1020            __stop___param - __start___param,
          1021            level, level,
          1022            NULL, &repair_env_string);
          1023 
          1024     trace_initcall_level(initcall_level_names[level]);
          1025     for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {
          1026         printk("### INITCALL BEGIN: level=%d fn=%ps ###\n", level, initcall_from_entry(fn));
          1027         do_one_initcall(initcall_from_entry(fn));
          1028         printk("### INITCALL END: level=%d fn=%ps ret=%d ###\n", level, initcall_from_entry(fn), ret);
          1029     }
          1030 }
      

      最後出問題的地方在 clk_disable_unused

      ### INITCALL END: level=6 fn=i2c_dev_init ret=0 ###
      ### INITCALL BEGIN: level=6 fn=sunxi_led_driver_init ###
      ### INITCALL END: level=6 fn=sunxi_led_driver_init ret=0 ###
      ### INITCALL BEGIN: level=6 fn=timer_led_trigger_init ###
      ### INITCALL END: level=6 fn=timer_led_trigger_init ret=0 ###
      ### INITCALL BEGIN: level=6 fn=defon_led_trigger_init ###
      ### INITCALL END: level=6 fn=defon_led_trigger_init ret=0 ###
      ### INITCALL BEGIN: level=6 fn=ashmem_init ###
      ### INITCALL END: level=6 fn=ashmem_init ret=0 ###
      ### INITCALL BEGIN: level=6 fn=sunxi_sid_driver_init ###
      ### INITCALL END: level=6 fn=sunxi_sid_driver_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=init_oops_id ###
      ### INITCALL END: level=7 fn=init_oops_id ret=0 ###
      ### INITCALL BEGIN: level=7 fn=pm_qos_power_init ###
      ### INITCALL END: level=7 fn=pm_qos_power_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=pm_debugfs_init ###
      ### INITCALL END: level=7 fn=pm_debugfs_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=wakeup_reason_init ###
      ### INITCALL END: level=7 fn=wakeup_reason_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=printk_late_init ###
      ### INITCALL END: level=7 fn=printk_late_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=init_srcu_module_notifier ###
      ### INITCALL END: level=7 fn=init_srcu_module_notifier ret=0 ###
      ### INITCALL BEGIN: level=7 fn=tk_debug_sleep_time_init ###
      ### INITCALL END: level=7 fn=tk_debug_sleep_time_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=fault_around_debugfs ###
      ### INITCALL END: level=7 fn=fault_around_debugfs ret=0 ###
      ### INITCALL BEGIN: level=7 fn=max_swapfiles_check ###
      ### INITCALL END: level=7 fn=max_swapfiles_check ret=0 ###
      ### INITCALL BEGIN: level=7 fn=prandom_reseed ###
      ### INITCALL END: level=7 fn=prandom_reseed ret=0 ###
      ### INITCALL BEGIN: level=7 fn=clk_debug_init ###
      ### INITCALL END: level=7 fn=clk_debug_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=sync_state_resume_initcall ###
      ### INITCALL END: level=7 fn=sync_state_resume_initcall ret=0 ###
      ### INITCALL BEGIN: level=7 fn=deferred_probe_initcall ###
      ### INITCALL END: level=7 fn=deferred_probe_initcall ret=0 ###
      ### INITCALL BEGIN: level=7 fn=ubi_init ###
      ### INITCALL END: level=7 fn=ubi_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=of_fdt_raw_init ###
      ### INITCALL END: level=7 fn=of_fdt_raw_init ret=0 ###
      ### INITCALL BEGIN: level=7 fn=clk_disable_unused ###
      

      忽略 CLK 錯誤檢查

      chosen {
          bootargs = "console=ttyS4,115200n8 earlycon=sbi ignore_loglevel loglevel=8 init=/init clk_ignore_unused";
      };
      

      終於進入載入 init 的階段,這才是 Kernel 完成開機的階段,當然中間忽略的問題還要一一找出來

      Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
      CPU: 0 PID: 1 Comm: swapper Not tainted 5.4.61 #5
      Call Trace:
      [<ffffffe0000211c4>] walk_stackframe+0x0/0x98
      [<ffffffe00002137c>] show_stack+0x2a/0x34
      [<ffffffe000287892>] dump_stack+0x20/0x28
      [<ffffffe000025882>] panic+0xec/0x27a
      [<ffffffe000000f22>] mount_block_root+0x214/0x27a
      [<ffffffe00000110a>] mount_root+0x86/0x90
      [<ffffffe000001288>] prepare_namespace+0x174/0x17e
      [<ffffffe000000b62>] kernel_init_freeable+0x174/0x1c8
      [<ffffffe000297ff8>] kernel_init+0x12/0xee
      [<ffffffe00001ff30>] ret_from_exception+0x0/0xc
      ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
      

      移除 CLK 錯誤檢查

      chosen {
          bootargs = "console=ttyS4,115200n8 earlycon=sbi ignore_loglevel loglevel=8 init=/init";
      };
      

      在 CLK 裡面添加 Debug Log

      1230 static void clk_disable_unused_subtree(struct clk_core *core)
      1231 {
      1232     struct clk_core *child;
      1233     unsigned long flags;
      1234 
      1235     printk(
      1236         "CLK %s: prepare=%d enable=%d protect=%d parent=%s\n",
      1237         core->name,
      1238         core->prepare_count,
      1239         core->enable_count,
      1240         core->protect_count,
      1241         core->parent ? core->parent->name : "NULL");
      

      定位到 PLL-CPU 問題

      ### CLK UNUSED END: pll-ddr0 ###
      CLK pll-cpux: prepare=0 enable=0 protect=0 parent=dcxo24M
      CLK riscv: prepare=0 enable=0 protect=0 parent=pll-cpux
      CLK riscv-axi: prepare=0 enable=0 protect=0 parent=riscv
      ### CLK UNUSED END: riscv-axi ###
      ### CLK UNUSED END: riscv ###
      CLK pll-cpux-div: prepare=0 enable=0 protect=0 parent=pll-cpux
      ### CLK UNUSED END: pll-cpux-div ###
      

      將 SUN8IW20_PLL_CPUX_REG 設定成 CLK_IS_CRITICAL

      $ vim drivers/clk/sunxi-ng/ccu-sun8iw20.c
          37 #define SUN8IW20_PLL_CPUX_REG       0x000
          38 static struct ccu_mult pll_cpux_clk = {
          39     .enable     = BIT(27),
          40     .lock       = BIT(28),
          41     .mult       = _SUNXI_CCU_MULT_MIN(8, 8, 12),
          42     .common     = {       
          43         .reg        = 0x000,
          44         .hw.init    = CLK_HW_INIT("pll-cpux", "dcxo24M",
          45                           &ccu_mult_ops,
          46                           CLK_SET_RATE_UNGATE | CLK_IS_CRITICAL),
          47     },
          48 };
      

      添加 BOOT 開機選項,然後使用 rootwait 故意讓 Kernel 停在等待 rootfs Ready 的狀態

      chosen {
          bootargs = "console=ttyS4,115200n8 earlycon=sbi ignore_loglevel loglevel=8 root=/dev/mmcblk0p1 rootwait rw rootfstype=fat init=/init";
      };
      

      Kernel 已經可以正常開機

      printk: console [ttyS4] enabled
      printk: bootconsole [sbi0] disabled
      printk: bootconsole [sbi0] disabled
      misc dump reg init
      brd: module loaded
      loop: module loaded
      i2c /dev entries driver
      ashmem: initialized
      Waiting for root device /dev/mmcblk0p1...
      

      司徒當初考慮使用 Tina-Linux 的原因是 Allwinner F133 相關的移植都已經完成, 我們應該可以很快就將 Kernel 移植使用, 但是目前看來, Tina-Linux 太高度客製化, 導致在還沒有很熟悉配置設定前, 可能需要花一些時間來除錯問題, 不過這個投資可能還是比從頭移植 Allwinner F133 到 main stream 上來得快一點~划算划算~哈

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒幫小志掌機規劃的Memory Layout
      10.jpg

      Device Tree保留這些區域避免被覆蓋

      memory@40000000 {
          device_type = "memory";
          reg = <0x0 0x40000000 0x0 0x04000000>; /* 64 MB */
      };
      
      reserved-memory {
          #address-cells = <2>;
          #size-cells = <2>;
          ranges;
      
          opensbi_reserved: opensbi@43e00000 {
              reg = <0x0 0x43e00000 0x0 0x00080000>; /* 512 KiB */
              no-map;
          };
      
          dtb_reserved: dtb@43d00000 {
              reg = <0x0 0x43d00000 0x0 0x00010000>; /* 64 KiB */
              no-map;
          };
      };
      

      開機後的Log

      [    0.000000] Linux version 5.4.61 (steward@debian) (riscv64-unknown-linux-gnu-gcc (C-SKY RISCV Tools V1.8.4 B20200702) 8.1.0, GNU ld (GNU Binutils) 2.32) #1 PREEMPT Fri Sep 11 07:05:18 CST 2026
      [    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
      [    0.000000] printk: bootconsole [sbi0] enabled
      [    0.000000] cma: Reserved 16 MiB at 0x0000000042c00000
      [    0.000000] Zone ranges:
      [    0.000000]   DMA32    [mem 0x0000000040000000-0x0000000043ffffff]
      [    0.000000]   Normal   empty
      [    0.000000] Movable zone start for each node
      [    0.000000] Early memory node ranges
      [    0.000000]   node   0: [mem 0x0000000040000000-0x0000000043cfffff]
      [    0.000000]   node   0: [mem 0x0000000043d10000-0x0000000043dfffff]
      [    0.000000]   node   0: [mem 0x0000000043e80000-0x0000000043ffffff]
      [    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x0000000043ffffff]
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒最終使出吃奶的力氣跟 AI 努力奮鬥後, 終於把這個 Crash 問題找出來, 首先看到 Crash PC位址是位於 0x205a12

      [    0.000000] sepc: ffffffe000205a12 ra : ffffffe00000b832 sp : ffffffe0006d7e10
      [    0.000000] sstatus: 0000000200000100 sbadaddr: 0000000010002080 scause: 0000000000000005
      

      定位副程式

      $ riscv64-unknown-linux-gnu-addr2line -e vmlinux -f -C 0xffffffe000205a12
          plic_toggle.isra.2
          irq-sifive-plic.c:?
      

      查看 Device Tree

      $ vim arch/riscv/boot/dts/sunxi/sun20iw1p1.dtsi
          plic0: interrupt-controller@10000000 {
      

      從 OpenSBI 列印 0x10002080 內容,發現內容都是 0

      M-mode read 0x10002080 => 0
      S-mode read 0x10002080 => 0
      

      開始定位權限問題,發現 PLIC_CTRL_REG 需要設定才可以存取 PLIC 內容
      1.jpg

      於是在 OpenSBI 跳轉到 Kernel 前,先設定 PLIC 存取權限,就可以解掉 Crash 問題

      *(volatile uint32_t *)0x101ffffc = 1;
      

      Baudrate 115200bps

      [    0.000000] Linux version 5.4.61 (steward@debian) (riscv64-unknown-linux-gnu-gcc (C-SKY RISCV Tools V1.8.4 B20200702) 8.1.0, GNU ld (GNU Binutils) 2.32) #1 PREEMPT Thu Sep 10 22:35:38 CST 2026
      [    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
      [    0.000000] printk: bootconsole [sbi0] enabled
      [    0.000000] cma: Reserved 16 MiB at 0x0000000047000000
      [    0.000000] Zone ranges:
      [    0.000000]   DMA32    [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000]   Normal   empty
      [    0.000000] Movable zone start for each node
      [    0.000000] Early memory node ranges
      [    0.000000]   node   0: [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000] elf_hwcap is 0x20112d
      [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32320
      [    0.000000] Kernel command line: console=ttyS4,115200n8 debug loglevel=7,initcall_debug=1 init=/init earlycon=sbi
      [    0.000000] Dentry cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
      [    0.000000] Inode-cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
      [    0.000000] Sorting __ex_table...
      [    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
      [    0.000000] Memory: 104568K/131072K available (4909K kernel code, 449K rwdata, 1909K rodata, 160K init, 234K bss, 10120K reserved, 16384K cma-reserved)
      [    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
      [    0.000000] rcu: Preemptible hierarchical RCU implementation.
      [    0.000000]  Tasks RCU enabled.
      [    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
      [    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
      [    0.000000] plic: mapped 200 interrupts with 1 handlers for 2 contexts.
      [    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
      [    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
      [    0.000125] sched_clock: 64 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
      [    0.008238] riscv_timer_clockevent depends on broadcast, but no broadcast function available
      [    0.021298] clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
      [    0.043416] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=240000)
      [    0.053948] pid_max: default: 32768 minimum: 301
      [    0.060648] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
      [    0.068014] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
      [    0.089491] ASID allocator initialised with 65536 entries
      [    0.095919] rcu: Hierarchical SRCU implementation.
      [    0.105840] devtmpfs: initialized
      [    0.342131] random: get_random_u32 called from bucket_table_alloc.isra.27+0x10a/0x12c with crng_init=0
      [    0.349219] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
      [    0.368572] futex hash table entries: 256 (order: 0, 6144 bytes, linear)
      [    0.384847] pinctrl core: initialized pinctrl subsystem
      [    0.401201] NET: Registered protocol family 16
      [    0.425372] DMA: preallocated 256 KiB pool for atomic allocations
      [    0.435759] cpuidle: using governor menu
      [    0.864565] rtc_ccu: sunxi ccu init OK
      [    0.943050] clock: sunxi ccu init OK
      [    0.955128] clock: sunxi ccu init OK
      [    1.281595] sun6i-dma 3002000.dma-controller: sunxi dma probed
      [    1.316158] iommu: Default domain type: Translated 
      [    1.322135] sunxi iommu: irq = 4
      [    1.336606] SCSI subsystem initialized
      [    1.343999] usbcore: registered new interface driver usbfs
      [    1.350350] usbcore: registered new interface driver hub
      [    1.356590] usbcore: registered new device driver usb
      [    1.362847] videodev: Linux video capture interface: v2.00
      [    1.376396] Advanced Linux Sound Architecture Driver Initialized.
      [    1.389471] Bluetooth: Core ver 2.22
      [    1.393475] NET: Registered protocol family 31
      [    1.397995] Bluetooth: HCI device and connection manager initialized
      [    1.404467] Bluetooth: HCI socket layer initialized
      [    1.409454] Bluetooth: L2CAP socket layer initialized
      [    1.414767] Bluetooth: SCO socket layer initialized
      [    1.421697] pwm module init!
      [    1.438662] g2d 5410000.g2d: Adding to iommu group 0
      [    1.447448] G2D: rcq version initialized.major:252
      [    1.459566] clocksource: Switched to clocksource riscv_clocksource
      [    1.644760] sun8iw20-pinctrl 2000000.pinctrl: initialized sunXi PIO driver
      [    1.679602] NET: Registered protocol family 2
      [    1.693720] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
      [    1.702402] TCP established hash table entries: 1024 (order: 1, 8192 bytes, linear)
      [    1.710560] TCP bind hash table entries: 1024 (order: 1, 8192 bytes, linear)
      [    1.718069] TCP: Hash tables configured (established 1024 bind 1024)
      [    1.725104] UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
      [    1.732064] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
      [    1.740853] NET: Registered protocol family 1
      [    1.771587] workingset: timestamp_bits=62 max_order=15 bucket_order=0
      [    1.892320] squashfs: version 4.0 (2009/01/31) Phillip Lougher
      [    1.901056] ntfs: driver 2.1.32 [Flags: R/W].
      [    2.831877] io scheduler mq-deadline registered
      [    2.836472] io scheduler kyber registered
      [    2.848914] [DISP]disp_module_init
      [    2.858080] disp 5000000.disp: Adding to iommu group 0
      [    3.319244] display_fb_request,fb_id:0
      [    3.325176] [DISP] Fb_copy_boot_fb,line:1449:
      [    3.325230] no boot_fb0
      [    3.335539] disp_al_manager_apply ouput_type:0
      [    3.341621] [DISP] lcd_clk_config,line:774:
      [    3.341861] disp 0, clk: pll(72000000),clk(72000000),dclk(6000000) dsi_rate(72000000)
      [    3.341861]      clk real:pll(288000000),clk(288000000),dclk(6000000) dsi_rate(0)
      [    3.366295] [DISP]disp_module_init finish
      [    3.376543] sunxi_sid_init()783 - insmod ok
      [    3.397789] uart uart0: uart0 supply uart not found, using dummy regulator
      [    3.407627] sw_uart_request_gpio()1273 - UART0 devm_pinctrl_get() failed! return -19
      [    3.415548] uart0: ttyS0 at MMIO 0x2500000 (irq = 18, base_baud = 1500000) is a SUNXI
      [    3.429644] sun8iw20-pinctrl 2000000.pinctrl: 2000000.pinctrl supply vcc-pb not found, using dummy regulator
      [    3.444651] uart uart4: uart4 supply uart not found, using dummy regulator
      [    3.454608] uart4: ttyS4 at MMIO 0x2501000 (irq = 22, base_baud = 1500000) is a SUNXI
      [    3.471324] printk: console [ttyS4] enabled setup baud 115200 parity n bits 8, flow n
      [    3.471324] printk: console [ttyS4] enabled
      [    3.480395] printk: bootconsole [sbi0] disabled
      [    3.480395] printk: bootconsole [sbi0] disabled
      [    3.499934] misc dump reg init
      [    3.523752] libphy: Fixed MDIO Bus: probed
      [    3.536492] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
      [    3.544162] sunxi-ehci: EHCI SUNXI driver
      [    3.554895] get ehci0-controller wakeup-source is fail.
      [    3.562094] sunxi ehci0-controller don't init wakeup source
      [    3.568767] [sunxi-ehci0]: probe, pdev->name: 4101000.ehci0-controller, sunxi_ehci: 0xffffffe00076b730, 0x:ffffffd004067000, irq_no:2e
      [    3.582892] [sunxi-ehci0]: Not init ehci0
      [    3.590936] get ehci1-controller wakeup-source is fail.
      [    3.598042] sunxi ehci1-controller don't init wakeup source
      [    3.604712] [sunxi-ehci1]: probe, pdev->name: 4200000.ehci1-controller, sunxi_ehci: 0xffffffe00076bec0, 0x:ffffffd00406b000, irq_no:31
      [    3.619088] sunxi-ehci 4200000.ehci1-controller: 4200000.ehci1-controller supply drvvbus not found, using dummy regulator
      [    3.634144] sunxi-ehci 4200000.ehci1-controller: 4200000.ehci1-controller supply hci not found, using dummy regulator
      [    3.649553] sunxi-ehci 4200000.ehci1-controller: EHCI Host Controller
      [    3.657523] sunxi-ehci 4200000.ehci1-controller: new USB bus registered, assigned bus number 1
      [    3.668805] sunxi-ehci 4200000.ehci1-controller: irq 49, io mem 0x04200000
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒目前已經將 Kernel 程式碼打包放到 https://github.com/steward-fu/gaviar, 這份 Kernel 是從 Tina-Linux 抽取出來的源代碼, 尚未做修改, 因此, 目前還無法正常開機進入系統, 司徒心想剛好紀錄整個除錯過程, 將來可以當作參考, 編譯步驟如下:

      $ cd
      $ wget https://github.com/steward-fu/website/releases/download/gaviar/thead_toolchain.tar.gz
      $ tar xvf thead_toolchain.tar.gz
      $ sudo mv thead /opt
      $ export PATH=/opt/thead/bin:$PATH
      
      $ git clone https://github.com/steward-fu/gaviar
      $ cd gaviar/kernel
      
      $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make distclean
      $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make gaviar_defconfig
      $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make menuconfig
      $ ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- make Image dtbs -j4
      
      $ sudo dd if=arch/riscv/boot/Image of=/dev/sdX   bs=1024 seek=400
      $ sudo dd if=arch/riscv/boot/dts/sunxi/board.dtb of=/dev/sdX bs=1024 seek=300
      

      開機問題如下:

      [    0.000000] Linux version 5.4.61 (steward@debian) (riscv64-unknown-linux-gnu-gcc (C-SKY RISCV Tools V1.8.4 B20200702) 8.1.0, GNU ld (GNU Binutils) 2.32) #3 PREEMPT Tue Sep 8 21:27:48 CST 2026
      [    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
      [    0.000000] printk: bootconsole [sbi0] enabled
      [    0.000000] cma: Reserved 16 MiB at 0x0000000047000000
      [    0.000000] Zone ranges:
      [    0.000000]   DMA32    [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000]   Normal   empty
      [    0.000000] Movable zone start for each node
      [    0.000000] Early memory node ranges
      [    0.000000]   node   0: [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x0000000047ffffff]
      [    0.000000] elf_hwcap is 0x20112d
      [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32320
      [    0.000000] Kernel command line: console=ttyS4,115200n8 debug loglevel=7,initcall_debug=1 init=/init earlycon=sbi
      [    0.000000] Dentry cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
      [    0.000000] Inode-cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
      [    0.000000] Sorting __ex_table...
      [    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
      [    0.000000] Memory: 104568K/131072K available (4909K kernel code, 448K rwdata, 1909K rodata, 160K init, 234K bss, 10120K reserved, 16384K cma-reserved)
      [    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
      [    0.000000] rcu: Preemptible hierarchical RCU implementation.
      [    0.000000]  Tasks RCU enabled.
      [    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
      [    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
      [    0.000000] Oops - load access fault [#1]
      [    0.000000] Modules linked in:
      [    0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 5.4.61 #3
      [    0.000000] sepc: ffffffe000205a12 ra : ffffffe00000b832 sp : ffffffe0006d7e10
      [    0.000000]  gp : ffffffe000741948 tp : ffffffe0006dc670 t0 : ffffffe0006d7d70
      [    0.000000]  t1 : 0000000000000005 t2 : 000000000000043f s0 : ffffffe0006d7ef0
      [    0.000000]  s1 : 0000000000000001 a0 : ffffffd000002080 a1 : 0000000000000002
      [    0.000000]  a2 : 0000000000000000 a3 : ffffffd000000000 a4 : 0000000000000020
      [    0.000000]  a5 : ffffffffffffffff a6 : ffffffe000577be0 a7 : 0000000000000000
      [    0.000000]  s2 : ffffffe0006f4bd8 s3 : ffffffe006e0b318 s4 : ffffffe00075d150
      [    0.000000]  s5 : 0000000000000000 s6 : 0000000000000002 s7 : ffffffe0005f3c78
      [    0.000000]  s8 : ffffffe000621920 s9 : ffffffe0007488a8 s10: 0000000000000001
      [    0.000000]  s11: 0000000000200000 t3 : ffffffd004000000 t4 : 9000000004fffcc7
      [    0.000000]  t5 : 0000000000000003 t6 : ffffffe0006d7d3c
      [    0.000000] sstatus: 0000000200000100 sbadaddr: 0000000010002080 scause: 0000000000000005
      [    0.000000] random: get_random_bytes called from init_oops_id+0x26/0x38 with crng_init=0
      [    0.000000] ---[ end trace 0000000000000000 ]---
      [    0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
      [    0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      呼叫的流程如下:
      9.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      Legacy SBI Interface有如下幾個Function,上面的那個main.s範例就是使用EID 0x01的sbi_console_putchar()印出字串,藉由上面的main.s範例,讓我們可以更了解SBI Interface的運作原理
      8.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      由於目前司徒目前使用LED閃爍程式替代Kernel,那是否代表可以修改這個LED閃爍程式來呼叫SBI Interface呢?答案肯定是可以的,程式碼如下:

          .global _start
          
          .text
          .long 0x05c0006f
          .byte 'e','G','O','N','.','B','T','0'
          .long 0x5f0a6c39
          .long 0x8000
          .long 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
         
      _start:
          la a0, msg
          call sbi_puts
      1:
          j 1b
       
      sbi_puts:
          mv t0, a0
      1:
          lbu a0, 0(t0)
          beqz a0, 2f
          li a7, 1
          ecall
          addi t0, t0, 1
          j 1b
      2:
          ret
       
      msg: .asciz "\n\nHello, world!\n"
          .end
      

      編譯、燒錄

      $ riscv64-unknown-linux-gnu-as -o main.o main.s
      $ riscv64-unknown-linux-gnu-ld -T main.ld -o main.elf main.o
      $ riscv64-unknown-linux-gnu-objcopy -O binary main.elf tmp.bin
      $ python3 gen_checksum.py tmp.bin main.bin
      $ sudo dd if=main.bin of=/dev/sdb bs=1024 seek=400
      

      開機後就可以從UART看到Hello, world!字串,如果想從屏幕上印出字串,只要修改lib/sbi/sbi_ecall_legacy.c,將#if 0改成#if 1後,重新編譯燒錄即可

      static int sbi_ecall_legacy_handler(unsigned long extid, unsigned long funcid,
                          const struct sbi_trap_regs *regs,
                          unsigned long *out_val,
                          struct sbi_trap_info *out_trap)
      {
          int ret = 0;
          struct sbi_tlb_info tlb_info;
          u32 source_hart = current_hartid();
          ulong hmask = 0;
       
          switch (extid) {
          case SBI_EXT_0_1_SET_TIMER:
      #if __riscv_xlen == 32
              sbi_timer_event_start((((u64)regs->a1 << 32) | (u64)regs->a0));
      #else
              sbi_timer_event_start((u64)regs->a0);
      #endif
              break;
          case SBI_EXT_0_1_CONSOLE_PUTCHAR:
              sbi_putc(regs->a0);
       
      #if 0
              /* Added by Steward for Gaviar handheld */
              void f133_slcd_putc(char ch);
              f133_slcd_putc(regs->a0);
      #endif
              break;
      

      7.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒目前幫小志掌機規劃的 MicroSD Layout 如下
      6.jpg

      檔案更新的方式如下

      $ sudo dd if=fw_jump.bin of=/dev/sdX bs=1024 seek=8
      $ sudo dd if=fw_jump.bin of=/dev/sdX bs=1024 seek=40
      $ sudo dd if=dtb         of=/dev/sdX bs=1024 seek=300
      $ sudo dd if=Image       of=/dev/sdX bs=1024 seek=400
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      為了讓OpenSBI具有開機執行的能力,司徒小改了一下fw_base.S,在程式進入點_gaviar加入判斷程式,如果是從0x20000的位址執行,則執行第一次初始化的動作,包含初始化UART、DRAM、MMC等,並且加載OpenSBI、Kernel、DTB到DRAM,至於為何是判斷位址0x20000呢?這是因為F133 BROM一上電後,會先從MicroSD 8KB位址處加載32KB程式到RAM 0x20000並且執行,所以司徒在0x20000處做標記(F133_TAG),接著等第二次執行時,這時候位址已經改成從0x41000000執行,所以就會直接跑_start的程式,因此我們需要兩份fw_jump.bin,一份是給0x20000用的(32KB),另一份則是完整的fw_jump.bin,司徒使用CONFIG=brom make用來編譯0x20000的fw_jump.bin,詳細的執行流程如下所示:
      5.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒列出幫小志掌機修改的部份
      3.jpg

      為何RISC-V需要有這些模式呢?可以從下圖得知,Linux/OS一般跑在S-Mode,但是當需要控制底層時,就需要切換M-Mode,這也是SBI介面的由來,當然,如果不用Linux/OS,就可以自己實做這一部份
      4.jpg

      P.S. 司徒目前幫小志掌機創見一個暫時 Git Repo (https://github.com/steward-fu/gaviar), 修改的 OpenSBI 也都在這個 Repo 裡面, 到時候, 等小志掌機都開發完成後, 司徒就會 Archive 到 https://github.com/steward-fu/website

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      由於全志針對F133的開機設計流程過於複雜,因此,為了可以更精簡以及方便維護程式碼,司徒特別針對小志掌機修改了一版OpenSBI,修改後的流程如下所示
      1.jpg

      編譯步驟:

      $ cd
      $ wget https://github.com/steward-fu/website/releases/download/gaviar/thead_toolchain.tar.gz
      $ tar xvf thead_toolchain.tar.gz
      $ sudo mv thead /opt
      $ export PATH=/opt/thead/bin:$PATH
      
      $ cd
      $ wget https://github.com/steward-fu/website/releases/download/gaviar/opensbi_20260906.tar.gz
      $ tar xvf opensbi_20260906.tar.gz
      $ cd opensbi
      
      $ export PLATFORM=allwinner/f133
      $ export CROSS_COMPILE=riscv64-unknown-linux-gnu-
      
      $ make distclean
      $ CONFIG=BROM make
      $ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_jump.bin fw_jump.bin
      $ dd if=fw_jump.bin of=payload/brom.bin bs=1024 count=32
      $ rm -rf fw_jump.bin
      
      $ make distclean
      $ make
      $ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_jump.bin payload/fw_jump.bin
      $ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_payload.bin payload/fw_payload.bin
      
      $ sudo dd if=payload/brom.bin    of=/dev/sdX bs=1024 seek=8
      $ sudo dd if=payload/fw_jump.bin of=/dev/sdX bs=1024 seek=40
      
      $ sudo dd if=dtb   of=/dev/sdX bs=1024 seek=300
      $ sudo dd if=Image of=/dev/sdX bs=1024 seek=400
      

      由於目前Kernel程式碼尚未完成,因此,司徒使用LED閃爍程式替代,開機後,OpenSBI會跑LED閃爍程式
      2.jpg

      Baudrate 115200bps

      [UART] Gaviar Handheld
      [DRAM] ZQ value = 0x2f
      [DRAM] get_pmu_exist() = -1
      [DRAM] ddr_efuse_type = 0xa
      [DRAM] trefi = 7.8ms
      [DRAM] single rank and full DQ!
      [DRAM] ddr_efuse_type = 0xa
      [DRAM] trefi = 7.8ms
      [DRAM] rank 0 row = 13 
      [DRAM] rank 0 bank = 4 
      [DRAM] rank 0 page size = 2 KB 
      [DRAM] DRAM BOOT DRIVE INFO: v0.33
      [DRAM] DRAM CLK = 528 MHz
      [DRAM] DRAM Type = 2 (2:DDR2, 3:DDR3)
      [DRAM] DRAMC read ODT  off
      [DRAM] DRAM ODT off
      [DRAM] ddr_efuse_type = 0xa
      [DRAM] DRAM SIZE = 64M
      [DRAM] dram_tpr4 = 0x0
      [DRAM] PLL_DDR_CTRL_REG = 0xf8002b00
      [DRAM] DRAM_CLK_REG = 0xc0000000
      [DRAM] MR2 = 0x0
      [DRAM] DRAM simple test OK
      [eMMC] MicroSD size = 59688MB
      [eMMC] Copy DTB to 0x41500000 (size = 65536)...
      [eMMC] Copy Kernel to 0x40008000 (size = 8388608)...
      [eMMC] Copy OpenSBI to 0x41000000 (size = 262144)...
      
      
      Welcome to Gaviar handheld
      OpenSBI v1.3
         ____                    _____ ____ _____
        / __ \                  / ____|  _ \_   _|
       | |  | |_ __   ___ _ __ | (___ | |_) || |
       | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
       | |__| | |_) |  __/ | | |____) | |_) || |_
        \____/| .__/ \___|_| |_|_____/|____/_____|
              | |
              |_|
      
      Platform Name             : Allwinner F133
      Platform Features         : medeleg
      Platform HART Count       : 1
      Platform IPI Device       : ---
      Platform Timer Device     : --- @ 0Hz
      Platform Console Device   : f133_uart
      Platform HSM Device       : ---
      Platform PMU Device       : ---
      Platform Reboot Device    : ---
      Platform Shutdown Device  : ---
      Platform Suspend Device   : ---
      Platform CPPC Device      : ---
      Firmware Base             : 0x41000000
      Firmware Size             : 194 KB
      Firmware RW Offset        : 0x20000
      Firmware RW Size          : 66 KB
      Firmware Heap Offset      : 0x28000
      Firmware Heap Size        : 34 KB (total), 2 KB (reserved), 8 KB (used), 23 KB (free)
      Firmware Scratch Size     : 4096 B (total), 728 B (used), 3368 B (free)
      Runtime SBI Version       : 1.0
      
      Domain0 Name              : root
      Domain0 Boot HART         : 0
      Domain0 HARTs             : 0*
      Domain0 Region00          : 0x0000000041000000-0x000000004101ffff M: (R,X) S/U: ()
      Domain0 Region01          : 0x0000000041020000-0x000000004103ffff M: (R,W) S/U: ()
      Domain0 Region02          : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
      Domain0 Next Address      : 0x0000000040008000
      Domain0 Next Arg1         : 0x0000000041500000
      Domain0 Next Mode         : S-mode
      Domain0 SysReset          : yes
      Domain0 SysSuspend        : yes
      
      Boot HART ID              : 0
      Boot HART Domain          : root
      Boot HART Priv Version    : v1.11
      Boot HART Base ISA        : rv64imafdcvx
      Boot HART ISA Extensions  : zicntr
      Boot HART PMP Count       : 8
      Boot HART PMP Granularity : 2048
      Boot HART PMP Address Bits: 38
      Boot HART MHPM Count      : 0
      Boot HART MHPM Mask       : 0x0
      Boot HART MIDELEG         : 0x0000000000000222
      Boot HART MEDELEG         : 0x000000000000b109
      Jump to kernel (0x40008000) ...
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒前些日子真的相當相當忙碌, 真的相當抱歉, 三年後的今天才又開始更新這個貼子~也相當欣慰 bankbank 還有持續聯繫司徒, 因此, 等後半段需要高手逆向 NeoGeo ROM 時, 司徒再來呼叫 bankbank, 哈, 這機器, 司徒已經三年沒有碰了, 因此, 每當夜裡總是會夢見暈哥在他的豪宅呼喚司徒, 該起來動一動了, 哈, 七月半, 不怕不怕, 哈, Gaviar 掌機還是處於開發階段, 因此, 最終樣貌會變成如何, 尚不得而知, 不過為了可以更方便的 Debug, 司徒決定先小改一下, 因此, OpenSCAD 畫了一個簡易底座

      $fn = 50;
       
      module panel(ch) {
          hull() {
              cd = 3;
              translate([0, 0, 0]) cylinder(ch, cd, cd);
              translate([81.85, 0, 0]) cylinder(ch, cd, cd);
              translate([0, 32.75, 0]) cylinder(ch, cd, cd);
              translate([81.85, 32.75, 0]) cylinder(ch, cd, cd);
          }
      }
       
      difference() {
          union() {
              difference() {
                  union() {
                      hull() {
                          ch = 4;
                          cd = 3;
                          translate([0, 0, 0]) cylinder(ch, cd, cd);
                          translate([81.85 + 2, 0, 0]) cylinder(ch, cd, cd);
                          translate([0, 32.75 + 2, 0]) cylinder(ch, cd, cd);
                          translate([81.85 + 2, 32.75 + 2, 0]) cylinder(ch, cd, cd);
                      }
       
                      hull() {
                          ch = 4;
                          cd = 1;
                          translate([-2, 0, 0]) cylinder(ch, cd, cd);
                          translate([16, 0, 0]) cylinder(ch, cd, cd);
                          translate([-2, -19, 0]) cylinder(ch, cd, cd);
                          translate([16, -19, 0]) cylinder(ch, cd, cd);
                      }
       
                      translate([69.85, 0, 0]) hull() {
                          ch = 4;
                          cd = 1;
                          translate([-2, 0, 0]) cylinder(ch, cd, cd);
                          translate([16, 0, 0]) cylinder(ch, cd, cd);
                          translate([-2, -19, 0]) cylinder(ch, cd, cd);
                          translate([16, -19, 0]) cylinder(ch, cd, cd);
                      }
                  }
       
                  translate([1, 1, 1]) panel(10);
       
                  // socket
                  translate([-2, -19, 0]) cube([18, 16, 4]);
                  translate([67.85, -19, 0]) cube([18, 16, 4]);
              }
       
              // type-c
              translate([-2, -19, 0]) difference() {
                  cube([19, 17, 4]);
                  translate([0.25, 0, 1]) cube([17.5, 16, 10]);
              }
       
              translate([67.85, -19, 0]) union() {
                  difference() {
                      cube([19, 17, 4]);
                      translate([0.25, 0, 1]) cube([17.5, 16, 10]);
                  }
              }
          }
       
          // type-c
          translate([10.75, -10, 1]) cube([5, 10, 5]);
          translate([-5, -15.75, 2.5]) cube([10, 10, 5]);
       
          // speaker
          translate([73.5, -10, 1]) cube([6, 10, 5]);
      }
      

      預計外觀是這樣
      1.jpg

      拓竹3D印表機還是相當給力
      2.jpg

      使用三秒膠將該上的元件都補齊
      3.jpg

      將CH340E的+5V連接到這裡
      4.jpg

      相當短的接線
      5.jpg

      正面
      6.jpg

      下邊
      7.jpg

      側邊
      8.jpg

      上邊
      9.jpg

      側邊
      10.jpg

      完美的開發機
      11.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      最近在幫 Miyoo Mini/Miyoo Mini Plus 移植 drastic NDS 模擬器, 目前已經快完善了, 這邊先晚一點更新 ~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99

      解開後, 使用 dd 命令燒錄到 MicroSD 卡後, 插入開機, 就會開始播放 Nokia 手機鈴聲~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99

      兩個問題問一下:

      1. 你是如何燒錄檔案到 MicroSD ?
      2. 因為預設是使用 adb shell 進入操作(USB Type-C), 你有使用 adb shell 試試嗎?
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @yofa2008
      v1.05 基於官方驅動做的整合包
      kbd 拿掉官方鍵盤驅動, 替換新的鍵盤驅動 (解決卡鍵問題)

      新的鍵盤驅動源碼:
      https://github.com/steward-fu/kernel/blob/jz4755_d8_linux-2.6.24_debian/drivers/input/keyboard/d88kbd.c

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 如何像MIYOO一样做图形介面

      @lovehex99
      會的, 還是會讓小志掌機可以跑像Miyoo的介面~

      小志掌機的帖子就是帶大家一起從頭做起, 主要是以原理的方式來解說讓大家知道移植的過程, 重點比較偏重於讓大家了解原理, 而不是直接給大家編譯好的固件使用, 所以更新會比較慢一點~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 如何像MIYOO一样做图形介面

      @lovehex99
      GMenuNX 是一個桌面介面, 應該是適合你的需求, 如果你的板子有如下條件就可以使用:

      1. framebuffer 顯示驅動
      2. SDL 1.2
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      你這個系統並不是我那個測試系統

      $ ls /
      bin  data  dev  etc  home  lib  lib64xthead  mnt  ok.wav  overlay  proc  pseudo_init  rdinit  riscv64-unknown-linux-gnu  rom  root  sbin  squashfs  sys  tmp  usr  var  www
      
      $ ls /etc/
      adb_profile      capabilities  crontabs      fw_env.config  inittab         modules.d        opkg       rc.common     shells                     uci-defaults  wpa_supplicant.conf
      asound.conf      cedarc.conf   device_info   group          main            mtab             passwd     rc.d          smt_cycle_sleep_wakeup.sh  udev
      banner           cedarx.conf   dnsmasq.conf  hotplug.d      mdev.conf       openwrt_release  profile    recorder.cfg  smt_sleep.sh               udhcpd.conf
      banner.failsafe  config        fstab         init.d         modules-boot.d  openwrt_version  rc.button  shadow        swupdate_public.pem        wifi
      
      $ cat /etc/main 
      #!/bin/sh
      amixer sset 'Headphone volume' 100%
      
      while [ 1 ]
      do
        aplay /ok.wav
      done
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      開機後, 插上 USB Type-C, 然後使用 adb devices 看看是否走找到裝置, 如果有找到裝置, 你可以用 adb shell 進去裝置, 然後執行 /etc/main 腳本~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      哈 ~ 由於個人原因, 我的 Gitee 帳號已經刪除了~所以, 我還是會繼續使用 GitHub ~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      iriver D88 UART 跟 音頻共用, 所以預設並沒有訊息輸出, 君正這個驅動比較舊, 建議使用 Win7 x86 系統刷機的, 另外, GitHub 是目前主要存放檔案的地方, 如果無法存取的話, 你只能請人幫忙搬移了~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @yofa2008

      官方系統
      https://steward-fu.github.io/website/umpc/d88/stock_setup.htm
       
      雙系統
      https://steward-fu.github.io/website/umpc/d88/stock_dual.htm
       
      RetroFW
      https://steward-fu.github.io/website/umpc/d88/retrofw_setup.htm
      1.jpeg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      Tina-Linux 預設的 GPT 是配置 16GB, 你的 MicroSD卡是 4GB, 請使用 16GB 以上的 MicroSD 測試

      Device      Start      End  Sectors  Size Type
      /dev/sdb1   73728    74231      504  252K Microsoft basic data
      /dev/sdb2   74232    74735      504  252K Microsoft basic data
      /dev/sdb3   74736    75239      504  252K Microsoft basic data
      /dev/sdb4   75240    95719    20480   10M Microsoft basic data
      /dev/sdb5   95720   144359    48640 23.8M Microsoft basic data
      /dev/sdb6  144360   166759    22400   11M Microsoft basic data
      /dev/sdb7  166760   176999    10240    5M Microsoft basic data
      /dev/sdb8  178176 31115263 30937088 14.8G Microsoft basic data
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      iriver D88 如果超過 25元, 就不建議入手~
       
      司徒接著說明一下 Boot Loader 移植的步驟,幸好司徒一開始沒有把話說死, 哈, 因為如果要重新移植一個新的 Boot Loader, 那可能要花上好幾個月, 哈, 所以司徒打算直接拿 Tina-Linux 來做說明就好, 真是太輕鬆了~哈
       
      BOOT0、OpenSBI 的 UART 腳位配置
      1.jpg
       
      U-Boot 的 UART 腳位配置
      2.jpg
       
      U-Boot 的 MicroSD 腳位配置
      3.jpg
       
      由於是 Boot Loader 移植,司徒有必要解說一下 Boot Loader 的用途是什麼,從下圖可以知道,最簡單的 Boot Loader 就是初始化 RAM、關閉中斷、關閉 Cache 後,將 Linux Kernel 載入 RAM,然後執行 Linux Kernel,這就是 Boot Loader 的主要工作~
      4.jpg
       
      但是 RISC-V 的 Boot Loader 其實稍微複雜一點點,如下圖所示,主要有三個部份組成 Boot Loader:LOADER(BOOT0)、OpenSBI、BOOTLAODER(U-Boot)
      5.jpg
       
      相較於 ARM,RISC-V 多了一個 OpenSBI,那 OpenSBI 是什麼呢?其實 OpenSBI 主要用來做權限轉移,從低權限切到高權限執行硬體的相關操作,雖然這部份可以實作在 Kernel 中,不過 RISC-V 看來有想要抽象化硬體的概念,一旦將硬體抽象化,代表將來只要實作 OpenSBI,就可以達到兼容執行在不同廠家的晶片上,不過司徒也是剛接觸 RISC-V,既然大家都這樣使用,那就只好跟隨大家~
      6.jpg
       
      司徒畫了一張 F133 的啟動流程,用來對照一下
      7.jpg
      步驟1. 上電後,F133 開始跑
      步驟2. F133 將晶片內部的 BROM 載入到 SRAM,然後從 SRAM 開始跑
      步驟3. BROM 接著開始尋找可以使用的媒介,小志掌機只有 MicroSD,因此,BROM 會從 MicroSD 的 8KB 位址載入程式到 SRAM 執行
      步驟4. F133 第一個執行的程式是 BOOT0
      步驟5. BOOT0 主要用來初始化 RAM 並且執行 OpenSBI
      步驟6. OpenSBI 執行後,會載入 U-Boot 並且將控制權交給 U-Boot
      步驟7. U-Boot 會從 MicroSD 載入 Linux Kernel、Device Tree 到 RAM,接著執行 Linux Kernel
      步驟8. Linux Kernel 會依據 BOOTCMD 載入 rootfs 並且將控制權交給 init 程式,init 是 Linux 作業系統的第一個執行程式
       
      由於 BOOT0 是第一個執行的程式,司徒額外畫了一個執行流程圖
      8.jpg
       
      雖然 Boot Loader 流程大致清楚,不過,司徒還是需要說一下,一開始提到的 UART 配置修改,究竟這些修改是設定到哪邊去呢?其實位於 lichee/brandy-2.0/spl/nboot/main/boot0_head.c

      const boot0_file_head_t  BT0_head = {
          {
              /* jump_instruction*/
              JUMP_INSTRUCTION,
              BOOT0_MAGIC,
              STAMP_VALUE,
      #ifdef ALIGN_SIZE_8K
              0x2000,
      #else
              0x4000,
      #endif
              sizeof(boot_file_head_t),
              BOOT_PUB_HEAD_VERSION,
              CONFIG_BOOT0_RET_ADDR,
              CONFIG_BOOT0_RUN_ADDR,
              0,
              {
              /*brom modify: nand-4bytes sdmmc-2bytes*/
              0, 0, 0, 0, '4', '.', '0', 0
              },
          },
      
          {
              /*__u32 prvt_head_size;*/
              0,
              /*char prvt_head_vsn[4];*/
              1,
              0,/* power_mode */
              {0},/* reserver[2]  */
              /*unsigned int     dram_para[32] ;*/
              {0},
              /*__s32 uart_port;*/
              0,
              /*normal_gpio_cfg   uart_ctrl[2];*/
              {
              {6, 2, 4, 1, 1, 0, {0} },/*PB8: 4--RX*/
              {6, 4, 4, 1, 1, 0, {0} },/*PB9: 4--TX*/
              },
              /*__s32 enable_jtag;*/
              0,
              /*normal_gpio_cfg    jtag_gpio[5];*/
              {{0}, {0}, {0}, {0}, {0} },
      

      Tina-Linux 使用 Patch 方式,將配置的數值更新到 boot0_header,雖然作法粗暴,不過卻是最簡單的配置替換作法,缺點就是不好獨立拆開使用
       
      如果想要手動編譯看看, 可以使用如下方式 (已經改好 UART 配置)

      $ cd
      $ git clone https://github.com/steward-fu/tina-linux_sdk
      $ cd tina-linux_sdk
      
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.001
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.002
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_toolchain.tar.gz
      $ tar xvf https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_toolchain.tar.gz
      $ 7za x https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.001
      
      $ source ./build/envsetup.sh
      $ lunch
          You're building on Linux
      
          Lunch menu... pick a combo:
               1. d1-h_nezha_min-tina
               2. d1-h_nezha-tina
               3. d1s_nezha-tina
      
          Which would you like? [Default d1s_nezha]:3
      
      $ make
      $ pack
          /home/steward/Data/tina-linux_sdk/out/d1s-nezha/tina_d1s-nezha_uart4.img
      
          pack finish
      

      BOOT0 位於:out/d1s-nezha/image/boot0_sdcard.fex (8KB 偏移位置)

      $ sudo dd if=out/d1s-nezha/image/boot0_sdcard.fex of=/dev/sdX bs=1024 seek=8
      

      OpenSBI、U-Boot 位於:out/d1s-nezha/image/boot_package.fex

      $ sudo dd if=out/d1s-nezha/image/boot_package.fex of=/dev/sdX bs=1024 seek=16400
      

      開機後的 Log (baudrate: 115200bps)

      [152]HELLO! BOOT0 is starting!
      [155]BOOT0 commit : 4654c3ec4
      [158]set pll start
      [160]fix vccio detect value:0xc0
      [163]periph0 has been enabled
      [166]set pll end
      [168][pmu]: bus read error
      [170]board init ok
      [172]ZQ value = 0x2f
      [174]get_pmu_exist() = -1
      [177]ddr_efuse_type: 0xa
      [179]trefi:7.8ms
      [181][AUTO DEBUG] single rank and full DQ!
      [185]ddr_efuse_type: 0xa
      [187]trefi:7.8ms
      [189][AUTO DEBUG] rank 0 row = 13 
      [193][AUTO DEBUG] rank 0 bank = 4 
      [196][AUTO DEBUG] rank 0 page size = 2 KB 
      [200]DRAM BOOT DRIVE INFO: V0.33
      [203]DRAM CLK = 528 MHz
      [205]DRAM Type = 2 (2:DDR2,3:DDR3)
      [208]DRAMC read ODT  off.
      [210]DRAM ODT off.
      [212]ddr_efuse_type: 0xa
      [215]DRAM SIZE =64 M
      [217]dram_tpr4:0x0
      [219]PLL_DDR_CTRL_REG:0xf8002b00
      [222]DRAM_CLK_REG:0xc0000000
      [225][TIMING DEBUG] MR2= 0x0
      [229]DRAM simple test OK.
      [231]dram size =64
      [233]card no is 0
      [235]sdcard 0 line count 4
      [237][mmc]: mmc driver ver 2021-04-2 16:45
      [247][mmc]: Wrong media type 0x0
      [250][mmc]: ***Try SD card 0***
      [268][mmc]: HSSDR52/SDR25 4 bit
      [271][mmc]: 50000000 Hz
      [273][mmc]: 3781 MB
      [275][mmc]: ***SD/MMC 0 init OK!!!***
      [331]Loading boot-pkg Succeed(index=0).
      [335]Entry_name        = opensbi
      [338]Entry_name        = u-boot
      [342]Entry_name        = dtb
      [344]mmc not para
      [346]Jump to second Boot.
      
      OpenSBI auto-t113-linux-V0.8-3-ge39e937
         ____                    _____ ____ _____
        / __ \                  / ____|  _ \_   _|
       | |  | |_ __   ___ _ __ | (___ | |_) || |
       | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
       | |__| | |_) |  __/ | | |____) | |_) || |_
        \____/| .__/ \___|_| |_|_____/|____/_____|
              | |
              |_|
      
      Platform Name          : T-HEAD Xuantie Platform
      Platform HART Features : RV64ACDFIMSUVX
      Platform Max HARTs     : 1
      Current Hart           : 0
      Firmware Base          : 0x41fc0400
      Firmware Size          : 75 KB
      Runtime SBI Version    : 0.2
      
      MIDELEG : 0x0000000000000222
      MEDELEG : 0x000000000000b1ff
      PMP0    : 0x0000000041fc0000-0
      
      U-Boot 2018.07 (Jun 26 2023 - 13:32:21 +0000) Allwinner Technology
      
      [00.414]DRAM:  64 MiB
      [00.416]Relocation Offset is: 01ee9000
      [00.420]secure enable bit: 0
      can't support chip type 24064
      [00.426]CPU=720 MHz,PLL6=600 Mhz,AHB=200 Mhz, APB1=100Mhz  MBus=300Mhz
      sunxi flash map init
      SPI ALL:   ready
      [00.437]flash init start
      [00.439]workmode = 0,storage type = 1
      [00.443][mmc]: mmc driver ver uboot2018:2021-12-20 13:35:00
      [00.448][mmc]: get sdc_type fail and use default host:tm1.
      [00.455][mmc]: can't find node "mmc0",will add new node
      [00.460][mmc]: fdt err returned <no error>
      [00.464][mmc]: Using default timing para
      [00.467][mmc]: SUNXI SDMMC Controller Version:0x50310
      [00.494][mmc]: card_caps:0x3000000a
      [00.497][mmc]: host_caps:0x3000003f
      [00.504]sunxi flash init ok
      [00.507]line:703 init_clocks
      [00.510]drv_disp_init
      request pwm success, pwm7:pwm7:0x2000c00.
      [00.532]drv_disp_init finish
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      [00.564]Loading Environment from SUNXI_FLASH... GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      Can't find env partition
      *** Warning - no device, using default environment
      
      Failed (-19)
      [00.604]boot_gui_init:start
      [00.607]set disp.dev2_output_type fail. using defval=0
      [00.635]set disp.fb0_rot_used fail. using defval=0
      [00.639]set disp.fb0_rot_degree fail. using defval=0
      [00.810]boot_gui_init:finish
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      partno erro : can't find partition bootloader
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get[00.868]LCD open finish
      _info_efi: *** ERROR: Invalid Backup GPT ***
      partno erro : can't find partition boot-resource
      [00.879]Get bootloader and boot-resource partition number fail!
      [00.884]out of usb burn from boot: not need burn key
      cann't get the boot_base from the env
      [00.903]Item0 (Map) magic is bad
      [00.906]the secure storage item0 copy0 magic is bad
      [00.911]Item0 (Map) magic is bad
      [00.914]the secure storage item0 copy1 magic is bad
      [00.918]Item0 (Map) magic is bad
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      partno erro : can't find partition private
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      [00.980]update part info
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      [01.009]update bootcmd
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid GPT ***
      GUID Partition Table Header signature is wrong: 0x0 != 0x5452415020494645
      part_get_info_efi: *** ERROR: Invalid Backup GPT ***
      [01.069]change working_fdt 0x42aa8da0 to 0x42a88da0
      disable nand error: FDT_ERR_BADPATH
      [01.079]The storage not support sample function
      No reserved memory region found in source FDT
      [01.113]update dts
      Hit any key to stop autoboot:  2
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      慢慢來~急什麼呢!哈哈~
       
      由於司徒臨時被插入一個很緊急的事件, 所以導致進度緩慢了一些, 而這個事件就是有人發現 iriver D88 只賣 25元, 真是太佛心了~所以司徒只好先抽身去搞 iriver D88 系統的東西, 實在是逼不得已~哈~不過, 目前應該是搞定了, 所以就分享一下結果給大家~
       
      司徒紀錄所有移植過程在這裡
      https://steward-fu.github.io/website/umpc.htm#d88
       
      所有源碼以及資源在這裡
      https://github.com/steward-fu/archives/releases/tag/d88
       
      4.png
       
      不錯的質感
      1.jpg
       
      下邊
      2.jpg
       
      耳機孔
      3.jpg
       
      上邊
      4.jpg
       
      MicroSD、MiniUSB
      5.jpg
       
      相當不錯的鍵盤
      6.jpg
       
      背面
      7.jpg
       
      背蓋
      8.jpg
       
      PCB
      9.jpg
       
      C面
      10.jpg
       
      LMS430HF15
      11.jpg
       
      屏FPC
      12.jpg
       
      鍵盤FPC
      13.jpg
       
      鍵盤
      14.jpg
       
      正面
      15.jpg
       
      JZ4755
      16.jpg
       
      STMPE2403、K9LBG08U0D
      17.jpg
       
      WM8731、AR1010
      18.jpg
       
      正面
      19.jpg
       
      A3V56S40ETP-G6
      20.jpg
       

      由於機器已經 10 年沒有開機, 因此, 經過 5A55, 萬光明 的努力, 已經可以成功還原官方系統
      1.jpeg
       
      司徒寫了一個雙系統啟動介面, 也可以透過 USB Gadget Serial (UART) 進去機器 Debug
      2.jpeg
       
      RetroFW 也移植完成, 聲音, 按鍵, 背光, 蓋上待機, 應該都可以正常運作了~由於官方鍵盤驅動有卡鍵及延遲等問題, 因此, 在 鉗工 的協助下, 我們逆向官方內核並且打了補丁, 修正官方驅動程式的問題~
      3.jpeg
       
      再來則是 詩諾比 的 Lava 移植
      1.jpg
      2.jpg
      3.jpg
      4.jpg
      5.jpg
      6.jpg
      7.jpg
      8.jpg
      9.jpg
      10.jpg
      11.jpg
      12.jpg
      13.jpg
      14.jpg
      15.jpg
      16.jpg
      17.jpg
      18.jpg
      0_0.jpg
      0_1.jpg
      0_2.jpg
      0_3.jpg
      0_4.jpg
      0_5.jpg
      0_6.jpg
      0_7.jpg
      0_10.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @xiaowenge
      感謝你提供資訊, 看來後續的超頻測試, 應該會很有看頭, 哈~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      正面
      1.jpg
       
      下邊
      2.jpg
       
      側邊
      3.jpg
       
      上邊
      4.jpg
       
      側邊
      5.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      第二版的機器(新增兩顆振動馬達)終於收到,感謝暈哥以及lovexulu的協助
      1.jpg
       
      在Pyra掌機的身旁,更是感覺小巧~
      2.jpg
       
      材質還不錯~
      3.jpg
       
      USB Type-C、耳機孔
      4.jpg
       
      電源開關
      5.jpg
       
      R、MicroSD、L
      6.jpg
       
      音量鍵
      7.jpg
       
      背面
      8.jpg
       
      A面
      9.jpg
       
      FPC
      10.jpg
       
      PCB
      11.jpg
       
      電源按鍵記得往下扳才能取出主板
      12.jpg
       
      正面
      13.jpg
       
      新增振動馬達的槽位
      15.jpg
       
      D1s (單核心RISC-V RV64GCV 533MHz, RAM 64MB)
      F133 (單核心 RISC-V RV64GCV 533MHz, RAM 64MB)
      T113-S3 (双核心 Cortex-A7 1.2GHz, RAM 128MB)
      以上 3 顆晶片的腳位都是相容腳位,因此,玩家也可以替換成高效能的 T113-S3 進行移植
      16.jpg
      17.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      屏可視角
      1.jpg
       
      下邊
      2.jpg
       
      側邊
      3.jpg
       
      上邊
      4.jpg
       
      側邊
      5.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒接著說明一下如何編譯 Tina-Linux 做聲音測試, 不過在編譯 Tina-Linux 之前, 我們先來看一下小志掌機的聲音電路
      1.jpg
      上圖是一般最常使用的聲音電路, 直接從晶片的聲音輸出(類比訊號)拉到功率放大器, 由於是類比訊號, 因此, 在走線的過程中, 很容易受到干擾, 這方面的佈線, 比較需要經驗的累積, 當然, 如果要有比較好的聲音效果, 一般建議使用專門的 Audio Codec 晶片, 而使用的介面可以使用 I2S or PCM, 訊號傳遞的部份使用數位訊號, 可以有效抑制雜訊, 如下圖所示, 不過缺點就是需要移植聲音驅動, 這部份也是比較費時~
      2.jpg
       
      司徒接著說明如何編譯 Tina-Linux for F133, 首先, 感謝 lovexulu 的協助,提供 Tina-Linux 的移植結果給司徒參考,由於 Tina-Linux 下載相當費時也比較麻煩,因此,司徒將其搬遷到 GitHub,編譯步驟如下:

      $ cd
      $ git clone https://github.com/steward-fu/tina-linux_sdk
      $ cd tina-linux_sdk
      
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.001
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.002
      $ wget https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_toolchain.tar.gz
      $ tar xvf https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_toolchain.tar.gz
      $ 7za x https://github.com/steward-fu/archives/releases/download/f133/tina-linux_sdk_dl.7z.001
      
      $ source ./build/envsetup.sh
      $ lunch
          You're building on Linux
      
          Lunch menu... pick a combo:
               1. d1-h_nezha_min-tina
               2. d1-h_nezha-tina
               3. d1s_nezha-tina
      
          Which would you like? [Default d1s_nezha]:3
      
      $ make
      $ pack
          /home/steward/Data/tina-linux_sdk/out/d1s-nezha/tina_d1s-nezha_uart4.img
      
          pack finish
      

       
      燒錄步驟:

      1. 下載 https://github.com/steward-fu/archives/releases/download/f133/PhoenixCardv4.2.7.7z 並且解壓縮
      2. 執行 PhoenixCard.exe
      3. 選擇好 Image、Start up 後,按下 Burn 開始燒錄
        3.jpg
         
        燒錄完成
        4.jpg
         
        預設分區如下圖,如果想要使用自己編譯的 buildroot 系統,只要替換到 rootfs 分區就可以,不過預設只有 25MB
        5.jpg
         
        對 /dev/sdb8 進行擴充,這樣就可以放檔案進入測試
        6.jpg
         
        開機後,可以直接使用 adb shell 進入 console, 接著就可以手動測試聲音部份~
      $ adb devices
          * daemon not running; starting now at tcp:5037
          * daemon started successfully
          List of devices attached
          20080411	device
      
      $ adb shell
          BusyBox v1.27.2 () built-in shell (ash)
      
          ------run profile file-----
           _____  _              __     _
          |_   _||_| ___  _ _   |  |   |_| ___  _ _  _ _
            | |   _ |   ||   |  |  |__ | ||   || | ||_'_|
            | |  | || | || _ |  |_____||_||_|_||___||_,_|
            |_|  |_||_|_||_|_|  Tina is Based on OpenWrt!
           ----------------------------------------------
           Tina Linux (Neptune, 61CC0487)
           ----------------------------------------------
          nodev	debugfs
          root@TinaLinux:/#
          root@TinaLinux:/# mount /dev/mmcblk0p8 /mnt/UDISK/
          root@TinaLinux:/# amixer sset 'Headphone volume' 100%
              Simple mixer control 'Headphone volume',0
                Capabilities: volume volume-joined
                Playback channels: Mono
                Capture channels: Mono
                Limits: 0 - 7
                Mono: 7 [100%]
          root@TinaLinux:/# aplay /mnt/UDISK/ok.wav 
              Playing WAVE '/mnt/UDISK/ok.wav' : Signed 16 bit Little Endian, Rate 11025 Hz, Mono
      

       
      如果不想手動編譯, 司徒這邊有做好的聲音測試檔案
      https://github.com/steward-fu/archives/releases/download/f133/sound_test.img.7z
      解開後, 使用 dd 命令燒錄到 MicroSD 卡後, 插入開機, 就會開始播放 Nokia 手機鈴聲~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒接著說明如何驗證按鍵功能,不過,在進行按鍵驗證之前,我們必須先了解一下按鍵的一些細節,這樣才會更了解按鍵特性,一般掌機使用的按鍵,大部分都是使用十字鍵、4 個功能鍵、SELECT、START、L、R 等按鍵,這也是早期 Nintendo 掌機的標準配備,而用來耦合按鍵和 PCB 的橋樑則是以導電膠、鍋仔片為大宗,這些看似簡單的東西,其實背後隱藏的許多設計的小細節,司徒嘗試來解說一下過程,下圖是 NDSL 掌機的 PCB 圖片
      1.jpg
      從上圖可以發現一個設計小細節,十字鍵的 PCB 缺口,它的長相跟 4 個功能鍵並不一致,這是因為十字鍵是連通按鍵(上下左右連在一起),而 4 個功能鍵則是各自分開的按鍵,因此,當十字鍵的其中一個方向被按下時,其餘方向也會受影響,因此 PCB 設計必須做些保護措施,避免鬼鍵的問題發生
       
      司徒使用如下圖片說明一下鬼鍵發生的過程,當十字鍵的下方向被按下時,左、右方向的按鍵也會稍微往下傾斜,所以最壞的情況下,系統將會收到下、左、右三個按鍵的訊息
      2.jpg
       
      十字鍵正常位置
      3.jpg
       
      下方鍵被按下時,左、右方向的按鍵也往下傾斜,說明導電膠會有誤觸到 PCB 的狀況發生
      4.jpg
       
      因此,為了避免十字鍵的鬼鍵問題,十字鍵的 PCB 缺口必須依照方向做適當排列,這樣就可以避免誤觸的問題,如下圖所示,十字鍵的左右按鍵會設計成上下導通,而十字鍵的上下按鍵則會設計成左右導通
      5.jpg
       
      至於鍋仔片,由於導通點是位於中央,因此,十字鍵的鬼鍵問題得以改善
      6.jpg
      7.jpg
       
      下圖是小志掌機使用的按鍵,由於按壓力道需要比較用力,因此,這種按鍵比較少用於掌機,不過,如果使用此類的按鍵,則需要注意彈跳的問題
      8.jpg
       
      一般按鍵使用彈片做導通,如下所示
      9.jpg
       
      因此,按下或者放開時,會有一段不穩定的彈跳時間,如下圖所示,這就是一般按鍵的彈跳問題
      10.jpg
       
      針對軟體的解法,可以延遲一段時間後,一般是 10ms,接著再判斷按鍵是否確實被按下
      11.jpg
       
      針對硬體的解法,最常見到的作法就是 RC 濾波,在按鍵旁邊加上電容,基於 10ms 計算,最常使用的是:10K 電阻 + 1uF 電容、47K 電阻 + 220nF 電容,透過電容的充、放電效應,來修飾爬升曲線
      12.jpg
      市面上,有些晶片已經有內建 Debounce 功能,這樣就可以很方便解決按鍵彈跳問題
       
      當然,有些掌機會使用類比搖桿當作十字鍵使用,如:Caanoo 掌機(類比電阻)、Neo Geo Pocket 掌機(4 顆按鍵),如果是使用 4 顆按鍵則可以使用如上的彈跳解法,但是,如果是使用類比電阻,則需要加入 Dead Zone 判斷,避免漂移問題
      13.jpg
       
      Dead Zone 一般用於無效區域設定,當類比搖桿的移動是處於這些區域時,則不會發送任何移動訊號,而類比搖桿最常遇到就是靜止不動時,搖桿自動漂移(鬼鍵問題),因此,如果使用類比搖桿當作十字鍵使用時,記得加入 Dead Zone 判斷,避免漂移問題發生
      15.jpg
      14.jpg
       
      按鍵電路
      16.jpg
       
      ASCII 字型
      17.jpg
       
      對應按鍵
      18.jpg
       
      main.s

          .global _start
       
          .equ GPIO_BASE,  0x02000000
          .equ PD_CFG0,    0x0090
          .equ PD_CFG1,    0x0094
          .equ PD_CFG2,    0x0098
          .equ PD_DAT,     0x00a0
          .equ PE_CFG0,    0x00c0
          .equ PE_CFG1,    0x00c4
          .equ PE_DAT,     0x00d0
       
          .equ LCD_RST,    (1 << 0)
          .equ LCD_WR,     (1 << 18)
          .equ LCD_RS,     (1 << 19)
          .equ LCD_RD,     (1 << 20)
          .equ LCD_CS,     (1 << 21)
          .equ LCD_BL,     (1 << 22)
       
          .equ _250MS,     50000000
          .equ _500MS,     100000000
          .equ _1S,        200000000
          .equ BG,         0x0000
          .equ FG,         0xffff
       
          .text
          .long 0x4000006f
          .byte 'e','G','O','N','.','B','T','0'
          .long 0x5F0A6C39
          .long 0x8000
          .long 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
         
          .org 0x0400
      _start:
          li t0, 0x00000000
          li a0, GPIO_BASE + PE_CFG0
          sw t0, 0(a0)
          li a0, GPIO_BASE + PE_CFG1
          sw t0, 0(a0)
       
          li t0, 0x11111111
          li a0, GPIO_BASE + PD_CFG0
          sw t0, 0(a0)
          li a0, GPIO_BASE + PD_CFG1
          sw t0, 0(a0)
          li a0, GPIO_BASE + PD_CFG2
          sw t0, 0(a0)
       
          li t0, 0xffffffff
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          jal lcd_rst
       
          li t0, 0xb2
          jal lcd_cmd
          li t0, 0x5c
          jal lcd_dat
          li t0, 0x5c
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
           
          li t0, 0xb7
          jal lcd_cmd
          li t0, 0x35
          jal lcd_dat
           
          li t0, 0x21
          jal lcd_cmd
           
          li t0, 0x11
          jal lcd_cmd
       
          li t0, _250MS
          jal delay
       
          li t0, 0xe0
          jal lcd_cmd
          li t0, 0xd0
          jal lcd_dat
          li t0, 0x06
          jal lcd_dat
          li t0, 0x0b
          jal lcd_dat
          li t0, 0x07
          jal lcd_dat
          li t0, 0x07
          jal lcd_dat
          li t0, 0x24
          jal lcd_dat
          li t0, 0x2e
          jal lcd_dat
          li t0, 0x32
          jal lcd_dat
          li t0, 0x46
          jal lcd_dat
          li t0, 0x37
          jal lcd_dat
          li t0, 0x13
          jal lcd_dat
          li t0, 0x13
          jal lcd_dat
          li t0, 0x2d
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
           
          li t0, 0xe1
          jal lcd_cmd
          li t0, 0xd0
          jal lcd_dat
          li t0, 0x02
          jal lcd_dat
          li t0, 0x06
          jal lcd_dat
          li t0, 0x09
          jal lcd_dat
          li t0, 0x08
          jal lcd_dat
          li t0, 0x05
          jal lcd_dat
          li t0, 0x29
          jal lcd_dat
          li t0, 0x44
          jal lcd_dat
          li t0, 0x42
          jal lcd_dat
          li t0, 0x38
          jal lcd_dat
          li t0, 0x14
          jal lcd_dat
          li t0, 0x14
          jal lcd_dat
          li t0, 0x2a
          jal lcd_dat
          li t0, 0x30
          jal lcd_dat
           
          li t0, 0x36
          jal lcd_cmd
          li t0, 0xb0
          jal lcd_dat
           
          li t0, 0x2a
          jal lcd_cmd
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x01
          jal lcd_dat
          li t0, 0x3f
          jal lcd_dat
           
          li t0, 0x2b
          jal lcd_cmd
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0xef
          jal lcd_dat
           
          li t0, 0x3a
          jal lcd_cmd
          li t0, 0x55
          jal lcd_dat
       
          li t0, 0x29
          jal lcd_cmd
           
          li t0, 0x2c
          jal lcd_cmd
       
      loop:
          li a0, GPIO_BASE + PE_DAT
          lw t0, 0(a0)
       
          li t1, (1 << 0)
          and t1, t1, t0
          bgtz t1, 0f
          li t0, 30
          j draw
      0:
          li t1, (1 << 7)
          and t1, t1, t0
          bgtz t1, 1f
          li t0, 31
          j draw
      1:
          li t1, (1 << 4)
          and t1, t1, t0
          bgtz t1, 2f
          li t0, 16
          j draw
      2:
          li t1, (1 << 6)
          and t1, t1, t0
          bgtz t1, 3f
          li t0, 17
          j draw
      3:
          li t1, (1 << 12)
          and t1, t1, t0
          bgtz t1, 4f
          li t0, 'B'
          j draw
      4:
          li t1, (1 << 11)
          and t1, t1, t0
          bgtz t1, 5f
          li t0, 'X'
          j draw
      5:
          li t1, (1 << 1)
          and t1, t1, t0
          bgtz t1, 6f
          li t0, 'L'
          j draw
      6:
          li t1, (1 << 10)
          and t1, t1, t0
          bgtz t1, 7f
          li t0, 'R'
          j draw
      7:
          li t1, (1 << 2)
          and t1, t1, t0
          bgtz t1, 8f
          li t0, 'Y'
          j draw
      8:
          li t1, (1 << 9)
          and t1, t1, t0
          bgtz t1, 9f
          li t0, 'T'
          j draw
      9:
          li t1, (1 << 8)
          and t1, t1, t0
          bgtz t1, 10f
          li t0, 'S'
          j draw
      10:
          li t1, (1 << 3)
          and t1, t1, t0
          bgtz t1, 11f
          li t0, 'M'
          j draw
      11:
          li t1, (1 << 13)
          and t1, t1, t0
          bgtz t1, 12f
          li t0, 'A'
          j draw
      12:
          li x8, 0
          li x9, 0
          jal set_pos
       
          li t6, 320 * 240
      bg:
          li t0, BG
          jal lcd_dat
          addi t6, t6, -1
          bgtz t6, bg
       
          li t0, _250MS
          jal delay
          j loop
       
      draw:
          sll t0, t0, 3
          la a3, font
          add a3, a3, t0
          li t6, 8
          li x8, 135
          li x9, 100
      nbyte:
          li t5, 8
          lb a4, 0(a3)
          jal set_pos
      byte:
          li t1, 0x80
          and t0, a4, t1
          sll a4, a4, 1
          beqz t0, w0
      w1:
          li t0, FG
          jal lcd_dat
          li t0, FG
          jal lcd_dat
          li t0, FG
          jal lcd_dat
          li t0, FG
          jal lcd_dat
          j next
      w0:
          li t0, BG
          jal lcd_dat
          li t0, BG
          jal lcd_dat
          li t0, BG
          jal lcd_dat
          li t0, BG
          jal lcd_dat
      next:
          addi t5, t5, -1
          bgtz t5, byte
       
          add a3, a3, 1
          add x9, x9, 2
          addi t6, t6, -1
          bgtz t6, nbyte
          j loop
       
      set_pos:
          move a2, ra
          li t0, 0x2a
          jal lcd_cmd
          li t0, 0
          jal lcd_dat
          move t0, x8
          jal lcd_dat
       
          li t0, 0x2b
          jal lcd_cmd
          li t0, 0
          jal lcd_dat
          move t0, x9
          jal lcd_dat
       
          li t0, 0x2c
          jal lcd_cmd
          jr a2
       
      lcd_wr:
          li a0, GPIO_BASE + PD_DAT
       
          li t4, 0x00ff
          and t2, t0, t4
          li t4, 0xff00
          and t3, t0, t4
          sll t2, t2, 1
          sll t3, t3, 2
       
          move t4, t1
          or t4, t4, t2
          or t4, t4, t3
          sw t4, 0(a0)
       
          li t0, LCD_WR
          or t4, t4, t0
          sw t4, 0(a0)
          jr ra
       
      lcd_dat:
          move a1, ra
          li t1, LCD_RS | LCD_RD | LCD_BL | LCD_RST
          jal lcd_wr
          jr a1
       
      lcd_cmd:
          move a1, ra
          li t1, LCD_RD | LCD_BL | LCD_RST
          jal lcd_wr
          jr a1
       
      lcd_rst:
          move a1, ra
          li t0, 0x00000000
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          li t0, _250MS
          jal delay
       
          li t0, 0xffffffff
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          li t0, _250MS
          jal delay
          jr a1
       
      delay:
          addi t0, t0, -1
          bgtz t0, delay
          jr ra
       
      font:
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
          .byte 0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C
          .byte 0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
          .byte 0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00
          .byte 0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00
          .byte 0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
          .byte 0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40
          .byte 0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00
          .byte 0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00
          .byte 0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00
          .byte 0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00
          .byte 0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00
          .byte 0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00
          .byte 0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00
          .byte 0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00
          .byte 0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00
          .byte 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00
          .byte 0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00
          .byte 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00
          .byte 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00
          .byte 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00
          .byte 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00
          .byte 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30
          .byte 0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00
          .byte 0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00
          .byte 0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00
          .byte 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00
          .byte 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00
          .byte 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00
          .byte 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00
          .byte 0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00
          .byte 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00
          .byte 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
          .byte 0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00
          .byte 0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00
          .byte 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
          .byte 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00
          .byte 0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00
          .byte 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00
          .byte 0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00
          .byte 0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF
          .byte 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00
          .byte 0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00
          .byte 0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00
          .byte 0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C
          .byte 0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00
          .byte 0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00
          .byte 0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00
          .byte 0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00
          .byte 0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06
          .byte 0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00
          .byte 0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00
          .byte 0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00
          .byte 0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C
          .byte 0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00
          .byte 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00
          .byte 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00
          .end
      

       
      完成
      19.jpg
      20.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @hsinyuwang
      你這個 GIF 圖片是相當好的展示 💃💃

      如果沒有 TE 腳位, 但要解決閃屏, 難度比較高一點, 不過還是有機會, 只是需要依靠觀察做微調, 幾個建議提供給你參考:

      1. 屏的初始化步驟到第一筆送出的像素必須固定住時間
      2. 傳送像素完成後, 中斷的處理必須固定住時間
      3. 利用 0x2c Command 去抓延遲間隔(每個 Frame 的間隔)

      依據這樣微調, 你有機會可以解決閃屏, 但是, 你如果是跑 Linux 系統, 難度就很高, 因為你應該是會被 Context Switch, 時間固定不住, RTOS 就有機會, 然後, MCU 在每片屏上的時間差異, 還需要考慮~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      司徒接下來介紹點屏的步驟,而在點屏之前,我們需要先了解一下屏的一些特性,這樣才可以跑順後面的開發,一般常說的 8080 屏、i80 屏、MCU 屏,指的就是傳輸資料的方式是使用 Intel 8080 的讀寫方式,其實早期是有兩大派別,分別是 Intel 8080 和 Motorola 6800,至於為何後來都用 Intel 8080,這應該不用多說,8080 和 6800 的格式可以從下圖知曉
      1.jpg
       
      那為何 i80 屏需要做初始化呢 ?因為裡面有專門用來做顯示處理的 MCU,因此,需要設定一些參數,如:width=320, height=240, fps=60 等參數,所以傳送給屏的資料會分成 Data 和 Command,當然,有些特殊制定規格的屏就不須要初始化,因為參數都已經寫死並且固定在 MCU 裡,不過,市面上比較難購買到這類產品
      2.jpg
      MCU 在顯示圖像時,會固定從 RAM(或稱呼:顯示記憶體、Graphic RAM、GRAM)讀取資料並顯示在屏上面
       
      那該如何初始化呢?目前市面上的屏大約可以分成兩種作法:1. 使用 DB0~DB15 傳送初始化 Data 和 Command, 2. 使用 I2C 傳送 Data 和 Command
      3.jpg
      兩種方式都可以達到初始化的目的,因此,在購買 i80 屏後,記得跟賣家索取初始化命令
       
      那問題來了,當我們正在刷屏(寫入 RAM)時,MCU 是否有可能正在讀取 RAM ?答案:沒錯,這種狀況一定會發生,假如寫入跟讀取沒有一個同步機制,那就會發生這種狀況,而這種狀況就是一般俗稱:撕裂、閃屏、Screen Tearing,司徒畫了一張流程圖,下圖是 MCU 讀取 RAM 並且顯示在屏上的流程
      4.jpg
       
      當 F133 透過 TCON_LCD i80 介面傳送資料給屏時(黃色的像素),由於共用同一塊 RAM,因此,RAM 的內容被更新成黃色,同時,屏的 MCU 也正在更新圖像到屏上,因此,變成第一個畫面(紅色)和第二個畫面(黃色)的交疊狀況,如下圖
      5.jpg
       
      那 i80 屏的撕裂問題如何解決呢?其實,一般 i80 屏都會拉出 TE/FRAME 腳位,這個腳位一般不使用,因為它跟時序高度相關,需要同步處理,同常 TE/FRAME 腳位都會懸空,不過,TE/FRAME 腳位其實是用來告知屏的 MCU 是否正在更新圖像(讀取 RAM),因此,為了避免撕裂,F133 TCON_LCD 可以在 MCU 空閒時,傳送更新的資料,這樣就可以避免撕裂
      6.jpg
       
      那是不是搞定 TE/FRAME 腳位後,屏就不會撕裂?答案:不是,因為,從模擬器、顯示驅動到硬件傳送都可能會有撕裂問題,所以要確保顯示品質,任何經過的地方都必須仔細處理才可以保證畫面不撕裂
      7.jpg
       
      根據司徒的經驗,在不同地方的所造成的撕裂,將會有不一樣的結果,下圖是司徒整理的有趣現象,模擬器因為是像素更新居多,因此,覆蓋一般是從起始像素開始,所以撕裂比較像是上下分層,而顯示驅動一般使用區塊複製,因此,撕裂是屬於左右分層,屏的撕裂,由於更新跟寫入是屬於追跑的現象,因此,撕裂屬於三角分層
      8.jpg
       
      LCD 電路
      9.jpg
      10.jpg
       
      main.s

          .global _start
       
          .equ GPIO_BASE,  0x02000000
          .equ PD_CFG0,    0x0090
          .equ PD_CFG1,    0x0094
          .equ PD_CFG2,    0x0098
          .equ PD_DAT,     0x00a0
       
          .equ LCD_RST,    (1 << 0)
          .equ LCD_WR,     (1 << 18)
          .equ LCD_RS,     (1 << 19)
          .equ LCD_RD,     (1 << 20)
          .equ LCD_CS,     (1 << 21)
          .equ LCD_BL,     (1 << 22)
       
          .equ _250MS,     50000000
          .equ _500MS,     100000000
          .equ _1S,        200000000
       
          .text
          .long 0x4000006f
          .byte 'e','G','O','N','.','B','T','0'
          .long 0x5F0A6C39
          .long 0x8000
          .long 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
         
          .org 0x0400
      _start:
          li t0, 0x11111111
          li a0, GPIO_BASE + PD_CFG0
          sw t0, 0(a0)
          li a0, GPIO_BASE + PD_CFG1
          sw t0, 0(a0)
          li a0, GPIO_BASE + PD_CFG2
          sw t0, 0(a0)
       
          li t0, 0xffffffff
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          jal lcd_rst
       
          li t0, 0xb2
          jal lcd_cmd
          li t0, 0x5c
          jal lcd_dat
          li t0, 0x5c
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
           
          li t0, 0xb7
          jal lcd_cmd
          li t0, 0x35
          jal lcd_dat
           
          li t0, 0x21
          jal lcd_cmd
           
          li t0, 0x11
          jal lcd_cmd
       
          li t0, _250MS
          jal delay
       
          li t0, 0xe0
          jal lcd_cmd
          li t0, 0xd0
          jal lcd_dat
          li t0, 0x06
          jal lcd_dat
          li t0, 0x0b
          jal lcd_dat
          li t0, 0x07
          jal lcd_dat
          li t0, 0x07
          jal lcd_dat
          li t0, 0x24
          jal lcd_dat
          li t0, 0x2e
          jal lcd_dat
          li t0, 0x32
          jal lcd_dat
          li t0, 0x46
          jal lcd_dat
          li t0, 0x37
          jal lcd_dat
          li t0, 0x13
          jal lcd_dat
          li t0, 0x13
          jal lcd_dat
          li t0, 0x2d
          jal lcd_dat
          li t0, 0x33
          jal lcd_dat
           
          li t0, 0xe1
          jal lcd_cmd
          li t0, 0xd0
          jal lcd_dat
          li t0, 0x02
          jal lcd_dat
          li t0, 0x06
          jal lcd_dat
          li t0, 0x09
          jal lcd_dat
          li t0, 0x08
          jal lcd_dat
          li t0, 0x05
          jal lcd_dat
          li t0, 0x29
          jal lcd_dat
          li t0, 0x44
          jal lcd_dat
          li t0, 0x42
          jal lcd_dat
          li t0, 0x38
          jal lcd_dat
          li t0, 0x14
          jal lcd_dat
          li t0, 0x14
          jal lcd_dat
          li t0, 0x2a
          jal lcd_dat
          li t0, 0x30
          jal lcd_dat
           
          li t0, 0x36
          jal lcd_cmd
          li t0, 0xb0
          jal lcd_dat
           
          li t0, 0x2a
          jal lcd_cmd
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x01
          jal lcd_dat
          li t0, 0x3f
          jal lcd_dat
           
          li t0, 0x2b
          jal lcd_cmd
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0x00
          jal lcd_dat
          li t0, 0xef
          jal lcd_dat
           
          li t0, 0x3a
          jal lcd_cmd
          li t0, 0x55
          jal lcd_dat
       
          li t0, 0x29
          jal lcd_cmd
           
          li t0, 0x2c
          jal lcd_cmd
       
          li t6, 320 * 80
      red:
          li t0, 0xf800
          jal lcd_dat
          addi t6, t6, -1
          bgtz t6, red
           
          li t6, 320 * 80
      green:
          li t0, 0x7e0
          jal lcd_dat
          addi t6, t6, -1
          bgtz t6, green
           
          li t6, 320 * 80
      blue:
          li t0, 0x1f
          jal lcd_dat
          addi t6, t6, -1
          bgtz t6, blue
      1:
          j 1b
       
      lcd_wr:
          li a0, GPIO_BASE + PD_DAT
       
          li t4, 0x00ff
          and t2, t0, t4
          li t4, 0xff00
          and t3, t0, t4
          sll t2, t2, 1
          sll t3, t3, 2
       
          move t5, t1
          or t5, t5, t2
          or t5, t5, t3
          sw t5, 0(a0)
       
          li t0, LCD_WR
          or t5, t5, t0
          sw t5, 0(a0)
          jr ra
       
      lcd_dat:
          move a1, ra
          li t1, LCD_RS | LCD_RD | LCD_BL | LCD_RST
          jal lcd_wr
          jr a1
       
      lcd_cmd:
          move a1, ra
          li t1, LCD_RD | LCD_BL | LCD_RST
          jal lcd_wr
          jr a1
       
      lcd_rst:
          move a1, ra
          li t0, 0x00000000
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          li t0, _250MS
          jal delay
       
          li t0, 0xffffffff
          li a0, GPIO_BASE + PD_DAT
          sw t0, 0(a0)
       
          li t0, _250MS
          jal delay
          jr a1
       
      delay:
          addi t0, t0, -1
          bgtz t0, delay
          jr ra
          .end
      

       
      完成
      11.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      一般建議先去搞系統再去做移植, 感觸會比較深, 提供一些想法給你:

      1. 將 FunKeys rootfs 替換到 i.MX6 上面 (解開覆蓋掉原本的)
      2. 應該可以從 UART (serial port) 看到一些東西
      3. 開始看看系統裡面有什麼東西, 破壞它
      4. 如果 i.MX6 有 Framebuffer Driver, 你應該就可以看到顯示的畫面

      做完上面的, 你這時候再去做移植或者重新編譯 OS, 將會有很深的體驗~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      請問是移植 FunKeys 系統嗎?目前我們的重心都是放在驗證硬件的工作上, 而移植模擬器、系統是屬於比較後期的工作, 所以目前我們無法確定會移植哪些東西, 目前能確定的是 GNGEO 模擬器一定會移植, 因為那是目前用來驗證逆向工程的模擬器~

      @aldfaaa
      這板塊竟然還有 QQ 群, 厲害了~
      司徒最近剛從撕逼群退出, 已經具備神龍護體、金剛不死之身, 你可以貼出 QQ群號, 讓我加入~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @hsinyuwang
      RetroArch 本身支援 MIPS32, 因此, 有蠻大的機會可以移植給 RISC-V 使用, 只是目前移植 RetroArch 不在我們的計畫中, 之後如果有時間, 或許可以評估看看, 感謝你的建議~

      https://buildbot.libretro.com/nightly/dingux/
      1.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      UART 是嵌入式開發最常用來除錯的一個介面, 因此, 在開始移植 Boot Loader 或者 Linux Kernel 之前, 必須先確保 UART 可以正常工作, 接下來, 司徒將說明如何驗證 UART 的輸出功能, 目前司徒是直接在 PCB 上面焊上排針
      1.jpg
       
      接線方式
      2.jpg
       
      UART 連接到 PB2, PB3
      8.jpg
       
      PB2, PB3
      19.jpg
       
      GPIO Multiplex Function
      20.jpg
       
      CCU 位址
      3.jpg
       
      APB1 (HOSC 24MHz)
      4.jpg
       
      Baudrate Table (24MHz)
      5.jpg
       
      UART4 位址
      6.jpg
       
      UART 暫存器
      7.jpg
       
      main.s

          .global _start
       
          .equ CCU_BASE,   0x02001000
          .equ GPIO_BASE,  0x02000000
          .equ UART4_BASE, 0x02501000
       
          .equ PB_CFG0,      0x0030
          .equ UART_BGR_REG, 0x090c
       
          .equ RBR,  0x0000
          .equ THR,  0x0000
          .equ DLL,  0x0000
          .equ DLH,  0x0004
          .equ IER,  0x0004
          .equ IIR,  0x0008
          .equ LCR,  0x000c
          .equ MCR,  0x0010
          .equ LSR,  0x0014
          .equ USR,  0x007c
           
          .text
          .long 0x4000006f
          .byte 'e','G','O','N','.','B','T','0'
          .long 0x5F0A6C39
          .long 0x8000
          .long 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
         
          .org 0x0400
      _start:
          li t0, 0x7700
          li a0, GPIO_BASE + PB_CFG0
          sw t0, 0(a0)
       
          li t0, (1 << 20) | (1 << 4)
          li a0, CCU_BASE + UART_BGR_REG
          sw t0, 0(a0)
       
          li t0, 0
          li a0, UART4_BASE + IER
          sw t0, 0(a0)
           
          li t0, 0
          li a0, UART4_BASE + MCR
          sw t0, 0(a0)
           
          li a0, UART4_BASE + LCR
          lw t0, 0(a0)
          li t1, (1 << 7)
          or t0, t0, t1
          sw t0, 0(a0)
       
          li t0, 13
          li a0, UART4_BASE + DLL
          sw t0, 0(a0)
           
          li t0, 0
          li a0, UART4_BASE + DLH
          sw t0, 0(a0)
           
          li a0, UART4_BASE + LCR
          lw t0, 0(a0)
          li t1, ~(1 << 7)
          and t0, t0, t1
          sw t0, 0(a0)
          lw t0, 0(a0)
          li t1, ~(0x1f)
          and t0, t0, t1
          li t1, 0x03
          or t0, t0, t1
          sw t0, 0(a0)
       
          la a1, hello
      0:
          li a0, UART4_BASE + LSR
          lw t0, 0(a0)
          li t1, (1 << 5)
          and t0, t0, t1
          beqz t0, 0b
       
          li t0, 0
          lb t0, 0(a1)
          li a0, UART4_BASE + THR
          sb t0, 0(a0)
       
          add a1, a1, 1
          bgtz t0, 0b
       
      1:
          j 1b
       
          .align
      hello: .asciz "Hello, world!"
          .end
      

       
      完成
      9.jpg
       
      不過, 目前小志掌機的外殼缺少 UART 洞口, 因此, 司徒基於暈哥的外殼, 使用 OpenSCAD 改造一下
       

      difference(){
          import("V20-bottom.stl");
          translate([-23, 10, -5]){
              cube([10, 10, 10]);
          }
      }
       
      translate([42.1, -4.5, -3]){
          cube([1.5, 3, 3]);
      }
      translate([42.1, 8.1, -3]){
          cube([1.5, 3, 3]);
      }
      

       
      UART位置
      10.jpg
       
      因為司徒的3D印表機精度不夠,只好補強音量鍵
      11.jpg
       
      3D印表機列印
      12.jpg
       
      還不錯
      13.jpg
       
      下邊
      14.jpg
       
      側邊
      15.jpg
       
      上邊
      16.jpg
       
      側邊
      17.jpg
       
      背面
      18.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      拿到打樣後的 PCB, 緊接著就需要進行驗證的動作, 一般在這個環節會有簡單的量產測試工具, 用來快速驗證某些模塊的功能是否正常, 這就是司徒接下來要介紹的東西, 如何寫出簡單的測試程序並下載到小志掌機做測試~
       
      由於需要用到平頭哥的編譯器 (司徒一開始以為平頭哥是一個人...), 因此, 需要手動下載安裝

      $ cd
      $ wget https://github.com/steward-fu/archives/releases/download/f133/Xuantie-900-gcc-linux-5.10.4-glibc-x86_64-V2.6.1-20220906.tar.gz
      $ tar xvf Xuantie-900-gcc-linux-5.10.4-glibc-x86_64-V2.6.1-20220906.tar.gz
      $ sudo mv Xuantie-900-gcc-linux-5.10.4-glibc-x86_64-V2.6.1 /opt/f133
      $ export PATH=/opt/f133/bin:$PATH
      $ riscv64-unknown-linux-gnu-gcc --version
          riscv64-unknown-linux-gnu-gcc (Xuantie-900 linux-5.10.4 glibc gcc Toolchain V2.6.1 B-20220906) 10.2.0
          Copyright (C) 2020 Free Software Foundation, Inc.
          This is free software; see the source for copying conditions.  There is NO
          warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      

       
      這階段的測試驗證將會使用 xfel 工具, 安裝方式如下

      $ cd
      $ git clone https://github.com/xboot/xfel
      $ cd xfel
      $ make
      $ sudo make install
      $ xfel --help
          xfel(v1.3.1) - https://github.com/xboot/xfel
      

       
      接下來, 司徒將會說明如何驗證 LED 部份, 確定你拿到的 PCB 是可以正常工作的, PE5 用來控制 LED,不過這顆 LED 是 WS2812C,它需要透過串列訊號控制, F133 本身就支援 LEDC 控制, 因此, 司徒將介紹如何透過 LEDC 點亮 LED
       
      1.jpg
       
      WS2812C 裡面有 R、G、B 三顆 LED,每顆 LED 用一個 Byte 來代表亮度,傳送的串列訊號如下:
       
      2.jpg
       
      因為只有一隻 DI 腳位,因此,對於時序有一些要求,如果要傳送 0,需要的時序是 T0H(300ns)+T0L(850ns),而傳送 1 則是 T1H(800ns)+T1L(400ns),結尾需要傳送 RET(>280us)
       
      3.jpg
       
      GPIO 位址
       
      4.jpg
       
      PE_CFG0 (0x0101 = LEDC-DO)
       
      5.jpg
       
      LEDC Clock 可以使用 HOSC、PeriPLL1x
       
      6.jpg
       
      CCU 位址
       
      7.jpg
       
      LEDC Clock
       
      8.jpg
       
      目前使用 HOSC
       
      9.jpg
       
      Clock Reset、Gating
       
      10.jpg
       
      目前關閉 DMA
       
      10.jpg
       
      main.s

          .global _start
        
          .equ CCU_BASE,     0x02001000
          .equ GPIO_BASE,    0x02000000
          .equ LEDC_BASE,    0x02008000
          .equ PE_CFG0,      0x00c0
          .equ _1S,          200000000
       
          .equ LEDC_CLK_REG,              0x0bf0
          .equ LEDC_BGR_REG,              0x0bfc
          .equ LEDC_CTRL_REG,             0x0000
          .equ LED_T01_TIMING_CTRL_REG,   0x0004
          .equ LEDC_DATA_FINISH_CNT_REG,  0x0008
          .equ LED_RESET_TIMING_CTRL_REG, 0x000c
          .equ LEDC_WAIT_TIME0_CTRL_REG,  0x0010
          .equ LEDC_DATA_REG,             0x0014
          .equ LEDC_DMA_CTRL_REG,         0x0018
          .equ LEDC_INT_CTRL_REG,         0x001c
          .equ LEDC_INT_STS_REG,          0x0020
          .equ LEDC_WAIT_TIME1_CTRL_REG,  0x0028
          
          .text
          .long 0x4000006f
          .byte 'e','G','O','N','.','B','T','0'
          .long 0x5F0A6C39
          .long 0x8000
          .long 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
          .long 0, 0, 0, 0, 0, 0, 0, 0
        
          .org 0x0400
      _start:
          li t0, (1 << 16) | (1 << 0)
          li a0, CCU_BASE + LEDC_BGR_REG
          sw t0, 0(a0)
       
          li t0, (1 << 31)
          li a0, CCU_BASE + LEDC_CLK_REG
          sw t0, 0(a0)
       
          li t0, 0x500000
          li a0, GPIO_BASE + PE_CFG0
          sw t0, 0(a0)
           
          li t0, (0x14 << 21) | (0x06 << 16) | (0x07 << 6) | (0x13 << 0)
          li a0, LEDC_BASE + LED_T01_TIMING_CTRL_REG
          sw t0, 0(a0)
           
          li t0, (0x1d4c << 16)
          li a0, LEDC_BASE + LEDC_DATA_FINISH_CNT_REG
          sw t0, 0(a0)
           
          li t0, (0x1d4c << 16)
          li a0, LEDC_BASE + LED_RESET_TIMING_CTRL_REG
          sw t0, 0(a0)
           
          li t0, (0xff << 0)
          li a0, LEDC_BASE + LEDC_WAIT_TIME0_CTRL_REG
          sw t0, 0(a0)
           
          li t0, 0
          li a0, LEDC_BASE + LEDC_DMA_CTRL_REG
          sw t0, 0(a0)
           
          li t0, 0
          li a0, LEDC_BASE + LEDC_INT_CTRL_REG
          sw t0, 0(a0)
       
          li t1, 0
          li t2, (1 << 12)
      0:
          li t0, (1 << 16) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 0)
          li a0, LEDC_BASE + LEDC_CTRL_REG
          sw t0, 0(a0)
       
          xor t1, t1, t2
          li a0, LEDC_BASE + LEDC_DATA_REG
          sw t1, 0(a0)
      1:
          li a0, LEDC_BASE + LEDC_INT_STS_REG
          lw t0, 0(a0)
          and t0, t0, 1
          beqz t0, 1b
          sw t0, 0(a0)
       
          li t0, _1S
          jal delay
          j 0b
       
      delay:
          addi t0, t0, -1
          bgtz t0, delay
          jr ra
          .end
      

       
      main.ld

      MEMORY {
          FLASH : ORIGIN = 0, LENGTH = 32M
      }
      
      SECTIONS {
          .text : { *(.text*) } > FLASH
          .rodata : { *(.rodata*) } > FLASH
          .bss : { *(.bss*) } > FLASH
      }
      

       
      gen_checksum.py

      import struct
      import os
       
      blocksize       = 0x4000
      stamp           = 0x5f0a6C39
      checksum_offset = 0x0c
      length_offset   = 0x10
      def pad_to_roundup(data: bytearray, boundary):
          excess = len(data) % boundary
          if excess:
              data += b'\0' * (boundary - excess)
       
      def main():
          # little endian + unsigned int 
          uint32iter = struct.Struct('<I')
          input_img = open(os.sys.argv[1], 'rb')
          rawbytes = bytearray(input_img.read())
          pad_to_roundup(rawbytes, blocksize)
          uint32iter.pack_into(rawbytes, checksum_offset, stamp)
          uint32iter.pack_into(rawbytes, length_offset, len(rawbytes))
          checksum = 0
          for uint32 in uint32iter.iter_unpack(rawbytes):
              checksum += uint32[0]
          uint32iter.pack_into(rawbytes, checksum_offset, checksum % (2**32))
          output_img = open(os.sys.argv[2], 'wb')
          output_img.write(rawbytes)
          output_img.close()
          input_img.close()
       
      if __name__ == '__main__':
          main()
      

       
      Makefile

      all:
      	riscv64-unknown-linux-gnu-as -o main.o main.s
      	riscv64-unknown-linux-gnu-ld -T main.ld -o main.elf main.o
      	riscv64-unknown-linux-gnu-objcopy -O binary main.elf tmp.bin
      	python3 gen_checksum.py tmp.bin main.bin
      
      run:
      	xfel ddr f133 && xfel write 0x40000000 main.bin && xfel exec 0x40000000
      
      clean:
      	rm -rf main.bin main.o main.elf tmp.bin
      

       
      連接 USB 到 PC

      $ lsusb
          Bus 002 Device 064: ID 1f3a:efe8 Onda (unverified) V972 tablet in flashing mode
      

       
      編譯

      $ make
          riscv64-unknown-linux-gnu-as -o main.o main.s
          riscv64-unknown-linux-gnu-ld -T main.ld -o main.elf main.o
          riscv64-unknown-linux-gnu-objcopy -O binary main.elf tmp.bin
          python3 gen_checksum.py tmp.bin main.bin
      

       
      下載

      $ sudo make run
          xfel ddr f133 && xfel write 0x40000000 main.bin && xfel exec 0x40000000
          100% [================================================] 16.000 KB, 383.004 KB/s
      

       
      完成
       
      12.jpg

      13.jpg
       
      開頭使用 0x4000006f,它是一個跳躍指令,因為第 4 個 Byte 後的資料會由 BROM 使用,至於司徒為何要保留長度 1024 (0x400)?因為 F133 的中斷向量總共有 223 個,中斷向量位址達到 0x380,因此,司徒就從 0x400 開始執行,至於 BROM 使用的資料格式,可以參考 https://github.com/Ouyancheng/FlatHeadBro/blob/master/boot0/boot0-header.c

      $ /opt/f133/bin/riscv64-unknown-linux-gnu-objdump -bbinary -mriscv:rv64 -D main.bin | head -n20
      
      main.bin:     file format binary
      
      
      Disassembly of section .data:
      
      0000000000000000 <.data>:
             0:    4000006f              j     0x400
             4:    4765                  li    a4,25
             6:    422e4e4f              fnmadd.d    ft8,ft8,ft2,fs0,rmm
             a:    3054                  fld   fa3,160(s0)
             c:    8c78                  0x8c78
             e:    9a24                  0x9a24
            10:    4000                  lw    s0,0(s0)
          ...
           3fe:    0000                  unimp
           400:    001002b7              lui   t0,0x100
           404:    02000537              lui   a0,0x2000
           408:    0c05051b              addiw a0,a0,192
           40c:    00552023              sw    t0,0(a0) # 0x2000000
      

       
      仔細的玩家應該可以發現,RISC-V 沒有 MIPS 的 Delay Slot,不需要在跳躍指令後面塞一個 nop 或者做指令排序,在開發 PSP 模擬器踩到的坑洞,看來 RISC-V 應該解決了,而所謂的 Delay Slot 指的就是如下狀況:

      lw   v0,4(v1)   # load word from address v1+4 into v0
      nop             # wasted load delay slot
      jr   v0         # jump to the address specified by v0
      nop             # wasted branch delay slot
      

      如果 jr 後面沒有插入 nop,則會跑飛,因為 jr 慢了一步執行(提取、解碼、執行),這種 Delay Slot 狀況是跟架構設計有關係,不過,司徒可以看到 RISC-V 的進步,真是感動流淚~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @whycan
      沒關係, 屏的部份先這樣就可以, 我之後在移植 Linux Kernel 時, 再來說明一下撕裂的原因, 感謝 😊

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @whycan
      沒問題~感謝 👍

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @whycan
      暈哥, 記得左右兩邊要加振動馬達, 然後 UART 的 PCB 腳位記得灌孔~😊

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      由於 KOF97 角色很多, 逆向需要花費相當多的時間去分析, 感謝 bankbank 的時間, 😁 , 於此同時, 司徒也會開始針對系統做移植, 而依照司徒的慣例, 都是先從硬件下手, 等待比較熟悉硬件後, 才會開始做移植的東西~感謝暈哥的開源文件, 將硬件原理圖開源出來, 原理圖設計得相當好, 如果有想要改版, 司徒這邊有一些想法, 或許可以參考一下

      1.jpg

      2.jpg

      3.jpg

      建議把CTRL拉到GPIO,方便用來控制雜音
      4.jpg

      MicroSD訊號沒有上拉電阻,因此,要注意PCB線長
      5.jpg

      GPIO都拉到獨立GPIO使用,避免鬼鍵問題,不過,可以使用晶片內部的提昇電阻,這樣可以減少電阻拉線
      6.jpg

      背光建議使用IC,避免壓降問題,屏使用RGB-565,建議找有TE(FRAME)的屏,這樣可以有效控制閃屏問題
      7.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovehex99
      感謝支持, 大家一起學習, 開發過程如果有遇到問題, 都歡迎提問~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @lovexulu
      有問題歡迎提問, 畢竟大家一起同樂比較有趣~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      逆向工程並非用於破壞攻擊的目的, 取而代之, 它是一個可以更了解細節的技術, 以這樣的角度, 則逆向變得更有趣, 就如同 bankbank 介紹的過程一樣, 透過查看變數而知道一些細節, 或許有些人不好意思詢問 bankbank 問題, 因此, 司徒帶大家走一下步驟:

      準備好 kof97.zip, neogeo.zip 後, 開啟 debug 模式

      $ mame kof97.zip -debug -resolution 320x240
      

      可以設定WatchPoint的地方
      1.jpg

      按下F11並且輸入wp 0x10843B,1,w,接著再按下F5執行
      2.jpg

      P2被攻擊後就會觸發中斷, 是不是很簡單~
      3.jpg
      4.jpg

      bankbank 透過尋找出一些位址, 讓大家知道模擬器是如何知道一些細節, 這也是振動需要判斷的數值, 這些看似很高深的技術, 其實透過說明, 大家都可以很容易上手, 因此, 如何讓懷舊遊戲變得更有趣, 有時候, 你也可以好好思考如何讓你喜愛的遊戲變得更好玩, 這才是樂趣所在, 當然, 大家更應該正向來看待逆向工程技術, 而基於一些原因, 我們盡量點到就好, 相信 bankbank 之後會為我們帶來更好玩的東西~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @kirin
      了解~感謝

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @livpo
      恭喜你答錯了💃💃
      取名小志是因為機器夠小而且使用全志晶片, 所以才使用小志這個名字, 至於未來是否還會有中志、大志掌機, 你可以建議給暈哥, 因為機器是由他們製作, 至於我們之後會玩什麼掌機, 目前不知道~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @Kirin
      另外, 想請問一下可以增加文字顏色這個功能嗎 ?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      @kirin
      相當感謝你們團隊的幫忙, 我跟 bankbank 在此致上謝意, 相信仇家已經準備提刀過來了~哈💃💃

      发布在 爱搞机专区
      steward
      司徒
    • 回复: Gaviar Handheld (小志掌機)

      Allwinner F133(D1s) 是 RISC-V 64位元架構, 就規格面來看, 應該是可以對應到 Ingenic JZ4760B 600MHz, 也就是 RS-97 掌機 (RetroGame), LDK GAME 掌機(小龍王)這一系列的等級, 當然 bankbank 和司徒是以 RISC-V 的軟件角度切入, 因此, 剛好可以趁機來看一下 RISC-V 跟 MIPS 的差距會有多大~

      簡要規格
      1.jpg

      TRIMUI SMART
      1.jpg

      GAME GEAR Micro
      2.jpg

      Arduboy
      3.jpg

      FunKeyS (對不起只剩下屍體)
      4.jpg

      舊 3DS
      5.jpg

      正面
      6.jpg

      USB-TypeC、耳機孔
      7.jpg

      電源開關
      8.jpg

      R、MicroSD、L
      9.jpg

      音量開關
      10.jpg

      背面
      11.jpg

      拆掉類比搖桿帽子才可以提起A面
      12.jpg

      提起 A 面
      13.jpg

      A 面
      14.jpg

      屏
      15.jpg
      16.jpg
      17.jpg

      PCB
      18.jpg

      電源開關往下才可以取出 PCB
      19.jpg

      電池物品禁止發送到台灣,因此,司徒需要自行焊接電池,這個空間相當適合其它改造
      20.jpg

      正面
      21.jpg

      Allwinner F133 (D1s)
      22.jpg

      发布在 爱搞机专区
      steward
      司徒
    • Gaviar Handheld (小志掌機)

      1.jpg
      中文名稱:小志掌機
      英文名稱:Gaviar Handheld
      開源方式:GPL License
      開發人員:bankbank (銀行銀行)、steward (司徒)
      開發目的:學習 RISC-V、體驗 KOF97 振動版
      面向玩家:喜愛 DIY 樂趣、掌機的玩家
      更新時間:由於我們的本業都跟這些無關, 只能利用閒餘時間研究, 因此, 每次更新的時間有可能長達一個月

      功能簡介:
      RISC-V 是一個趨勢, 只是目前比較少有掌機面市, 因此, bankbank 和司徒將會以軟件面向, 從頭做起, 並且將這些過程紀錄下來, 以供日後參考, 除了可以學習 RISC-V 的軟件開發之外, bankbank 也會進行 KOF97 遊戲逆向分析, 分析的目的並不是要修改 KOF97 ROM, 而是要讓模擬器知道哪些角色的招式可以去驅動振動馬達, 因為讓 KOF97 具備振動功能是 bankbank 跟司徒的夢想, KOF97 振動版相信可以再度勾起童年的快樂回憶, 由於逆向遊戲比較敏感, 因此, 盡量以點到為止

      購買事項:
      小志掌機是由暈哥製作並提供給 bankbank 和司徒,因此,如果想要購買的人可以去找暈哥,bankbank 和司徒並沒有參與任何販賣事項,司徒跟 bankbank 只是以開源的方向來研究這台掌機,當然所有的資源都是公開,因此,司徒比較建議玩家從 GitHub 下載並且自己動手列印製作,這樣會更有樂趣

      資料倉庫:
      https://github.com/Strugglemeat/gngeo
      https://github.com/steward-fu/archives/releases/tag/f133

      預計流程:

      • 逆向 KOF97
      • 硬件說明
      • 硬件功能確認
        • 點屏
        • 按鍵
        • 聲音
      • 移植 Boot Loader
      • 移植 Linux Kernel
        • 屏幕驅動
        • 按鍵驅動
        • 聲音驅動
        • 電池驅動
      • 建置 Linux OS
      • 移植 GNGEO 模擬器
        • 支援振動功能

      結帖時間:視情況而定
      贊助連結:
      https://store.steampowered.com/search/?developer=bankbank&ndl=1
      https://play.google.com/store/apps/details?id=com.stylus.cavesweeper&gl=US&pli=1

      注意事項:
      由於此貼屬於開源專案, 因此, 司徒的仇家預計將前來尋仇, 這部分需要請管理員幫忙過濾惡意攻擊的言論, 如果無法有效制止, 此帖將提早結束, 我們將繼續漂流尋找下一個可以撰寫文章的地方

      關於 bankbank (銀行銀行):
      romhacker and retro gamedev bankbank ( bankbank.net ), very focused on 68000, also interested in SH-2. my favorite game is Tetris the Grand Master 2 Plus. my dream is to make games and hacks that many people can enjoy. YouTube: https://www.youtube.com/bankbank

      關於 steward (司徒):
      喜歡惡搞掌機, 賦予掌機另一種生命力, 藉由惡搞掌機來回味童年的美好, 誰叫掌機要惡搞當年的我

      前言:
      司徒相當高興可以遇到 Hack ROM 的高手 bankbank, bankbank 除了喜愛 Hack ROM 之外, 也熱愛 DIY 的樂趣, bankbank 本身可以閱讀簡單的中文, 因此, 如果需要詢問 bankbank 問題, 請盡量使用簡單的中文詞彙或者使用英文發問, 由於這個開源專案是從頭做起, 難免會遇到無法預期的開發問題, 加上 bankbank 和司徒的本業並非這個領域, 因此, 只能利用瑣碎時間研究, 基於熱愛掌機所發起的開源專案, 因此, 請不要催促開發進度

      振動原理簡介:
      左、右馬達用來代表左、右的方向, 此例使用大門武郎的岚之山, 大絕招一發動時, 同時振動馬達
      2.png

      右摔時,右邊振動馬達開始振動
      3.png

      左摔時,左邊振動馬達開始振動
      4.png

      发布在 爱搞机专区
      steward
      司徒
    • 回复: MQ-T113使用xboot驱动GT911时中断无法使用

      @Ary_Ye
      I2C 訊號需要先確定再去看中斷問題, 就我的測試, I2C 應該是可以正常工作, 所以你可以往硬件問題排錯, 例如:I2C 上拉電阻等問題, 或者延長 XBoot 對 GT911 的 Reset 時間~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: MQ-T113使用xboot驱动GT911时中断无法使用

      @Ary_Ye XBoot在T113-S3上的中斷設定確實有問題, 分析如下:

      文章有點長,可以直接拉到底下看workaround方式 💃💃💃

      司徒終於從冰箱找到芒果派T113-S3開發板
      1.jpg

      司徒手上並沒有GT911觸控板,因此,司徒使用Arduino Micro模擬GT911訊號
      2.jpg

      Arduino腳位:
      3.jpg

      T113-S3腳位:
      4.jpg

      司徒將GT911 I2C訊號接到T113-S3 I2C-2,GT911的中斷則是接到T113-S3 PB6(手動觸發)
      5.jpg

      杜邦線連接
      6.jpg

      Arduino模擬GT911程式

      #include <Wire.h>
       
      unsigned short rx = 0;
      void receiveEvent(int howMany) {
        if(howMany == 2){
          rx = Wire.read();
          rx<<= 8;
          rx|= Wire.read();
        }
        else{
          while (Wire.available()) {
            Wire.read();
          }
        }
        Serial.print("howMany:");
        Serial.print(howMany);
        Serial.print("\n");
        Serial.print("0x");
        Serial.print(rx, HEX);
        Serial.print("\n");
      }
       
      unsigned short x = 0;
      unsigned short y = 0;
      void requestEvent() {
        switch(rx){
        case 0x8047:
          Wire.write(byte(0x01));
          break;
        case 0x8140:
          Wire.write('A');
          Wire.write('B');
          Wire.write('C');
          Wire.write('D');
          break;
        case 0x8144:
          Wire.write(byte(0x12));
          Wire.write(byte(0x34));
          break;
        case 0x814e:
          Wire.write(byte(0x80 | 1));
          break;
        case 0x814f:
          Wire.write(byte(0x01));
          Wire.write(byte(x));
          Wire.write(byte(x >> 8));
          Wire.write(byte(y));
          Wire.write(byte(y >> 8));
          Wire.write(byte(0x00));
          Wire.write(byte(0x00));
          Wire.write(byte(0x00));
          x+= 1;
          y+= 1;
          break;
        }
        rx = 0;
      }
       
      void setup() {
        Wire.begin(20);
        Wire.onReceive(receiveEvent);
        Wire.onRequest(requestEvent);
        Serial.begin(115200);
       
      }
       
      void loop() {
        delay(100);
      }
      

      src/arch/arm32/mach-t113s3/romdisk/boot/mangopi.json

      524     "i2c-t113:2@0x02502800": {
      525         "clock-name": "bus-i2c2",
      526         "clock-frequency": 400000,
      527         "reset": 466,
      528         "sda-gpio": 133,
      529         "sda-gpio-config": 4,
      530         "scl-gpio": 132,
      531         "scl-gpio-config": 4
      532     },
      
      662     "ts-gt911": {
      663         "i2c-bus": "i2c-t113.2",
      664         "slave-address": 20,
      665         "interrupt-gpio": 38,
      666         "interrupt-gpio-config": 14,
      667         "reset-gpio": 34,
      668         "reset-gpio-config": 1
      669     },
      

      src/arch/arm32/mach-t113s3/driver/ts-gt911.c

       75 static bool_t gt911_write(struct i2c_device_t * dev, u16_t reg, u8_t * buf, int len)
       76 {
       77     struct i2c_msg_t msg;
       78     u8_t mbuf[256];
       79
       80     if(len > sizeof(mbuf) - 1)
       81         len = sizeof(mbuf) - 1;
       82     mbuf[0] = (reg >> 8) & 0xff;
       83     mbuf[1] = (reg >> 0) & 0xff;
       84     memcpy(&mbuf[2], buf, len);
       85
       86     msg.addr = dev->addr;
       87     msg.flags = 0;
       88     msg.len = 32;//len + 2;
       89     msg.buf = &mbuf[0];
       90
       91     if(i2c_transfer(dev->i2c, &msg, 1) != 1)
       92         return FALSE;
       93     return TRUE;
       94 }
      
      134 static bool_t gt911_initial(struct i2c_device_t * dev)
      135 {
      136     u8_t cfg;
      137     u8_t id[4];
      138     u8_t ver[2];
      139
      140     if(!gt911_read(dev, GT911_CONFIG_DATA, &cfg, 1))
      141         return FALSE;
      142     if(!gt911_read(dev, GT911_PRODUCT_ID, &id[0], 4))
      143         return FALSE;
      144     if(!gt911_read(dev, GT911_FIRMWARE_VERSION, &ver[0], 2))
      145         return FALSE;
      146
      147     LOG("GT911 Version: %c%c%c%c(0x%02x%02x)(0x%02x)\r\n", id[0], id[1], id[2], id[3], ver[1], ver[0], cfg);
      148     return gt911_send_cfg(dev, (u8_t *)gt911_config_data, ARRAY_SIZE(gt911_config_data));
      149 }
      
      151 static void gt911_interrupt(void * data)
      152 {
      153     struct input_t * input = (struct input_t *)data;
      154     struct ts_gt911_pdata_t * pdat = (struct ts_gt911_pdata_t *)input->priv;
      155     u8_t status, buf[40];
      156     u8_t * p;
      157     int count, i;
      158     int id, x, y;
      159
      160     LOG("%s++\r\n", __func__);
      161     disable_irq(pdat->irq);
      162     for(i = 0; i < 5; i++)
      163     {
      164         pdat->node[i].valid = 0;
      165     }
      166     if(gt911_read(pdat->dev, GT911_STATUS, &status, 1) && (status & (1 << 7)))
      167     {
      168         count = status & 0x0f;
      169         if(count > 0 && count < 5)
      170         {
      171             if(gt911_read(pdat->dev, GT911_COOR_ADDR, &buf[0], count << 3))
      172             {
      173                 for(i = 0; i < count; i++)
      174                 {
      175                     p = &buf[i << 3];
      176                     id = p[0];
      177                     x = (p[2] << 8) | (p[1] << 0);
      178                     y = (p[4] << 8) | (p[3] << 0);
      179                     LOG("%s, count:%d, x:%d, y:%d\r\n", __func__, count, x, y);
      ...
      209     enable_irq(pdat->irq);
      210     LOG("%s--\r\n", __func__);
      211 }
      

      編譯、下載

      $ make CROSS_COMPILE=arm-linux-gnueabihf- PLATFORM=arm32-t113s3
      $ xfel ddr t113-s3
      $ xfel write 0x40000000 output/xboot.bin
      $ xfel exec 0x40000000
      

      Arduino GT911可以正常工作,但是,中斷PB6無法被觸發

      [    0.260214]GT911 Version: ABCD(0x3412)(0x01)
      [    0.287074]Probe device 'ts-gt911.0' with ts-gt911
      

      為了方便測試,司徒將PB6加上提昇電阻並且改成Falling中斷觸發

      282     LOG("%s, gpio:%d, gpiocfg:%d, irqnum:%d\r\n", __func__, gpio, gpiocfg, irq);
      283     if(gpio >= 0)
      284     {
      285         if(gpiocfg >= 0)
      286             gpio_set_cfg(gpio, gpiocfg);
      287         gpio_set_pull(gpio, GPIO_PULL_UP);
      288     }
      289     request_irq(pdat->irq, gt911_interrupt, IRQ_TYPE_EDGE_FALLING, input);
      

      PB6=GPIO38、GPIOCFG=14、IRQ=262,這個IRQ應該是抓錯了?

      [    0.266982]ts_gt911_probe, gpio:38, gpiocfg:14, irqnum:262
      

      7.jpg

      src/arch/arm32/mach-t113s3/romdisk/boot/mangopi.json

      388     "irq-gic400@0x03020000": { "interrupt-base": 32, "interrupt-count": 224 },
      389     "irq-t113-gpio@0x02000220": { "interrupt-base": 224, "interrupt-count":  8, "interrupt-parent":  101 },
      390     "irq-t113-gpio@0x02000240": { "interrupt-base": 256, "interrupt-count":  8, "interrupt-parent":  103 },
      391     "irq-t113-gpio@0x02000260": { "interrupt-base": 288, "interrupt-count": 23, "interrupt-parent":  105 },
      392     "irq-t113-gpio@0x02000280": { "interrupt-base": 320, "interrupt-count": 14, "interrupt-parent":  107 },
      393     "irq-t113-gpio@0x020002a0": { "interrupt-base": 352, "interrupt-count":  7, "interrupt-parent":  109 },
      394     "irq-t113-gpio@0x020002c0": { "interrupt-base": 384, "interrupt-count": 16, "interrupt-parent":  111 },
      

      src/driver/interrupt/interrupt.c

      191 bool_t request_irq(int irq, void (*func)(void *), enum irq_type_t type, void * data)
      192 {
      193     struct irqchip_t * chip;
      194     int offset;
      195
      196     if(!func)
      197         return FALSE;
      198
      199     chip = search_irqchip(irq);
      200     if(!chip)
      201         return FALSE;
      202
      203     offset = irq - chip->base;
      204     if(chip->handler[offset].func != null_interrupt_function) {
      205         return FALSE;
      206     }
      207
      208     LOG("%s, irq:%d, chip->base:%d, offset:%d\r\n", __func__, irq, chip->base, offset);
      

      XBoot修改到的中斷地址是屬於PC6,但是司徒的中斷是設定成PB6

      [    0.272634]request_irq, irq:262, chip->base:256, offset:6
      

      src/arch/arm32/mach-t113s3/driver/irq-t113-gpio.c

       92     addr = pdat->virt + GPIO_INT_CFG0 + ((offset >> 3) << 2);
       93     val = read32(addr);
       94     val &= ~(0xf << ((offset & 0x7) << 2));
       95     val |= ((cfg & 0x7) << ((offset & 0x7) << 2));
       96     write32(addr, val);
       97
       98     LOG("%s, addr:%p, type:%d, offset:%d\r\n", __func__, addr, type, offset);
      

      果然修改到PC6 0x02000240

      [    0.268151]ts_gt911_probe, gpio:38, gpiocfg:14, irqnum:262
      [    0.273812]request_irq, irq:262, chip->base:256, offset:6
      [    0.279202]irq_t113_gpio_settype, addr:0x02000240, type:3, offset:6
      [    0.285373]Probe device 'ts-gt911.0' with ts-gt911
      

      PB6是位於0x02000230
      8.jpg

      Workaround (src/driver/interrupt/interrupt.c)

       62 static struct irqchip_t * search_irqchip(int irq)
       63 {
       64     struct device_t * pos, * n;
       65     struct irqchip_t * chip;
       66
       67     list_for_each_entry_safe(pos, n, &__device_head[DEVICE_TYPE_IRQCHIP], head)
       68     {
       69         chip = (struct irqchip_t *)(pos->priv);
       70         if(irq == 230){
       71             if(chip->base == 224){
       72                 return chip;
       73             }
       74             continue;
       75         }
       76         if((irq >= chip->base) && (irq < (chip->base + chip->nirq)))
       77             return chip;
       78     }
       79     return NULL;
       80 }
      
      191 bool_t request_irq(int irq, void (*func)(void *), enum irq_type_t type, void * data)
      192 {
      193     struct irqchip_t * chip;
      194     int offset;
      195
      196     if(!func)
      197         return FALSE;
      198
      199     if(irq == 262){
      200         irq-= 32;
      201     }
      

      修改後,就可以正常中斷觸發並且回報觸控點

      [    0.260653]GT911 Version: ABCD(0x3412)(0x01)
      [    0.266935]ts_gt911_probe, gpio:38, gpiocfg:14, irqnum:262
      [    0.272326]request_irq, irq:230, chip->base:224, offset:6
      [    0.277959]irq_t113_gpio_settype, addr:0x02000220, type:3, offset:6
      [    0.299301]Probe device 'ts-gt911.0' with ts-gt911
      [    0.304091]Probe device 'g2d-t113.0' with g2d-t113
      [    0.310178]Probe device 'fb-t113-rgb.0' with fb-t113-rgb
      [    0.315658]Probe device 'console-uart.0' with console-uart
      [    0.322001]mount /private with 'ram' filesystem
      Press any key to stop auto boot:  0.000
      xboot: /# 
      [    5.434256]gt911_interrupt++
      [    5.440101]gt911_interrupt, count:1, x:1, y:1
      [    5.446270]gt911_interrupt--
      [    9.054572]gt911_interrupt++
      [    9.060405]gt911_interrupt, count:1, x:2, y:2
      [    9.066782]gt911_interrupt--
      
      发布在 爱搞机专区
      steward
      司徒
    • 回复: MQ-T113使用xboot驱动GT911时中断无法使用

      @ary_ye 不過, 從你的圖片看來, 這個 I2C 訊號也不太像是 SCL, SDA 的訊號 ? 這樣你還可以從 xboot 拿到觸控的資料 ?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: MQ-T113使用xboot驱动GT911时中断无法使用

      目前 XBOOT 有支援 GT-911 的平台如下:

      ./src/arch/riscv64/mach-f133/driver/ts-gt911.c
      ./src/arch/arm32/mach-t113s3jd/driver/ts-gt911.c
      ./src/arch/arm32/mach-t113s3/driver/ts-gt911.c
      ./src/arch/arm32/mach-z11s/driver/ts-gt911.c
      

      手上剛好有 F133 板子, 但是我沒有 GT-911 觸控板, 如果需要或許可以用 Arduino 模擬~
      1.jpg

      不過, 找問題的方式應該大同小異, 因此, 我用 F133 說一下,
      json 檔案確定有設定成 okay

      "ts-f133@0x02009c00": {
              "clock-name": "link-tpadc",
              "interrupt": 78, 
              "reset": 1136,
              "median-filter-length": 5,
              "mean-filter-length": 5,
              "calibration": [168012, -57, -137136144, -344, 8674, -2840431, 65536],
              "status": "okay"
          },  
      
          "ts-gt911": {
              "i2c-bus": "i2c-f133.2",
              "slave-address": 20, 
              "interrupt-gpio": 35, 
              "interrupt-gpio-config": 14, 
              "reset-gpio": 34, 
              "reset-gpio-config": 1,
              "status": "okay"
          }, 
      

      然後埋一些 LOG, 先確定觸控板有被正確配置

      static bool_t gt911_initial(struct i2c_device_t * dev)
      {
          u8_t cfg;
          u8_t id[4];
          u8_t ver[2];
      
          if(!gt911_read(dev, GT911_CONFIG_DATA, &cfg, 1)) {
              LOG("GT911: failed to read config data\n");
              return FALSE;
          }   
          if(!gt911_read(dev, GT911_PRODUCT_ID, &id[0], 4)) {
              LOG("GT911: failed to read product id\n");
              return FALSE;
          }   
          if(!gt911_read(dev, GT911_FIRMWARE_VERSION, &ver[0], 2)) {
              LOG("GT911: failed to read firmware version\n");
              return FALSE;
          }   
      
          LOG("GT911 Version: %c%c%c%c(0x%02x%02x)(0x%02x)", id[0], id[1], id[2], id[3], ver[1], ver[0], cfg);
          return gt911_send_cfg(dev, (u8_t *)gt911_config_data, ARRAY_SIZE(gt911_config_data));
      }
      

      埋好 LOG 之後, 下載測試

      $ make CROSS_COMPILE=/opt/f133/bin/riscv64-unknown-linux-gnu- PLATFORM=riscv64-f133
      $ xfel ddr f133 && xfel write 0x40000000 output/xboot.bin && xfel exec 0x40000000
      

      因為沒有連接 GT-911, 因此, 在讀取 config data 時就會出錯

      [    0.261738]GT911: failed to read config data
      

      因此, 幾個找問題的方式提供給你參考 (跟著訊號的方向找問題就可以)

      1. 確定觸控的資訊可以被正確讀取到, 你應該可以從 UART 看到 "GT911 Version: xxx"
      2. 如果可以讀取到觸控資訊, 那可以往中斷腳位的方向找問題, 例如:沒接好或者被拉高拉低等問題
      发布在 爱搞机专区
      steward
      司徒
    • 回复: MQ-T113使用xboot驱动GT911时中断无法使用

      @ary_ye 這邊是愛搞機器的專區, 可以請問你這個是什麼遊戲機嗎?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 【复刻麦当劳游戏机】用 ST7789V LCD 在 lichee pi nano (f1c100s)

      No, you need to fix your hardware firstly. Here is the LCD pinout for Lichee Nano:
      1.jpg

      And here is your LCD panel:
      1.png

      发布在 Linux
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @lovexulu 好的~感謝你的資訊~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @aldfaaa 不瞞你說~我也是這樣覺得, 應該不用太折騰 RISC-V 指令集~哈 🏇🏇

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 【复刻麦当劳游戏机】用 ST7789V LCD 在 lichee pi nano (f1c100s)

      Here is the schematic of PocketGo handheld: https://github.com/steward-fu/archives/releases/download/pocketgo/doc_schematic.pdf

      It seems the pinout are not same as PocketGo. So, you cannot replace it directly.
      1.jpg

      发布在 Linux
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @lovexulu 你對全志晶片好熟悉~小弟給大佬請安💃💃

      V851S這顆晶片也很適合使用, 我希望的需求是:
      QFP包裝
      兩核心(CPU >= 400MHz)
      RAM >= 64MB
      WiFi
      BT

      因為我希望開機有兩種OS模式, 其中一個如果有WiFi協助是最好, 不知道全志有這樣的晶片嗎 ?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 【复刻麦当劳游戏机】用 ST7789V LCD 在 lichee pi nano (f1c100s)

      @bankbank long time no see. 💃
      Please post the pinout information of the LCD panel and then we can decide how to connect it to F1C100S.

      发布在 Linux
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @lovexulu 感謝你的資訊, 不過 CV1800B 似乎找不到手冊~

      來個比較表看一下

      • BL808
        CPU RV64GCV(480MHz) + RV32GCP(320MHz) + RV32EMC(160MHz)
        RAM 64MB(PSRAM)
        2D DMA
        WiFi + BT

      • R128
        CPU RV64GCV(600MHz) + Cortex-M33(240MHz)
        RAM 32MB(PSRAM)
        G2D
        WiFi + BT

      • F133
        CPU RV64GCV(533MHz)
        RAM 64MB(DDR2)
        G2D

      BL808 那個 2D DMA 真的蠻屌的, 位移、旋轉、 2D搬移, 這功能真的很適合做遊戲機, 全志 R128 如果把 M33 替換成 F1C100S IP, 繼續延續 F1C100S 生命力, 那就真的很棒, 可惜沒有~

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @aldfaaa 在 可以在這裡寫非全志晶片的文章嗎 ? 中说:

      膜拜司徒大佬~😊 请多指教

      感謝支持~我不是大佬, 大佬是 xboot, 哈~只是 xboot 最近可能太忙了~這 R128 進度怎麼如此慢?哈哈~

      司徒最近翻開冰箱才發現全志贈送的哪吒開發板已經半年沒上電了~自己購買的芒果派F133也是半年沒摸了, 心裡有些愧疚, 這才想說來整理一下 RISC-V 的東西:https://steward-fu.github.io/website/mcu.htm#risc-v

      所以才發現便宜又大碗的 BL808, 哈, 所以想說找點樂趣玩玩, 不過 RISC-V 幾乎是 MIPS 那派人搞的, 司徒心想應該跟 MIPS 差不多, 這讓司徒突然想起以前的君正, 哈~就碼農角度來看, 除了多核心, 司徒目前還看不出優勢在哪 ?

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      其實司徒應該是要拿 xboot 正在研究的 R128 去跟 BL808 比較才對, 而不是拿 F133, 這下有趣了~ 🕺🕺
      1.jpg
      2.jpg

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      @newcastle 是地~正是小弟本人~💃💃

      雖然之前說不玩寨機, 不過我還是偷偷玩, 哈哈~只是以後我不再釋出任何研究成果, 就我自己玩而已, 哈~

      目前是玩 TRIMUI SMART寨機 (Allwinner S3 + RAM 128MB + eMMC 8GB + RTL8723DS), 目前還在逆向研究中, 預期要讓它跑 Linux Kernel + 精簡客製化的 Debian 桌面, 主要是想跑 WINE(arm32) 的逆向遊戲, 網址:https://steward-fu.github.io/website/handheld.htm#trimui-smart

      然後, 之前有一個特別的想法, 想做一台特別的掌機, 用來懷念小時候的樂趣, 目前可能鎖定 RISC-V 晶片, 只是用哪家, 目前還在觀望, 所以我才發貼問一下是否可以寫其它家晶片的問題~哈

      发布在 爱搞机专区
      steward
      司徒
    • 回复: 可以在這裡寫非全志晶片的文章嗎 ?

      太好了~司徒就知道全志是一個可以包容萬物的好廠商~哈 👯👯

      博流智能太不厚道了, 竟然出這種殺手等級的晶片, 感覺就是針對 ESP32 和 F133 而來, 這顆晶片的價格以及功能, 確實蠻屌的, 如果生態搞起來, 那真的要飛龍在天了~而為了可以更瞭解敵人, 司徒才想說來看看這顆 BL808 到底有多屌, 司徒的出發點是好意, 全志可別誤會司徒原意~哈哈

      當然, 也感謝全志願意讓司徒在此寫一些東西~哈 !

      发布在 爱搞机专区
      steward
      司徒
    • 可以在這裡寫非全志晶片的文章嗎 ?

      想問一下, 如果在這裡寫非全志晶片的文章可以嗎 ?例如:博流智能BL808 ? 應該不會被分屍吧~ 🚴

      发布在 爱搞机专区
      steward
      司徒
    • 1 / 1