野火指南者F103开发板之三: 自制QMK键盘固件的 EEPROM 和 RGB 矩阵
前言
在文章 [1]和 文章 [2] 中, 分别介绍了 bootloader 和 qmk 核心功能的移植.
这篇文章会稍微深入一些, 介绍自己一步一步了解和熟悉 stm32f103 的过程, 摸索之后配置好了键盘更多功能.
这篇文章中, 将通过阅读相关 datasheet, 了解 stm32 的基础知识, 给固件加上更多功能:
- 外置持久化存储
i2c eeprom
- 基于 dma 和 pwm 的 RGB Matrix.
源码仓库 https://github.com/hitsmaxft/qmk-kb-bheznz
给键盘加上持久记忆特性
qmk固件里需要存储当前的配置状态, 比如 rgb 当前使用的主题等, 为了在拔掉电源的情况下, 也能持久化记住配置.
开发板的持久化记录数据方式, 除了像固件这样刷写进去, 还有动态修改的方式.
在这里我们需要开启 qmk 中的 eeprom 特性.
基于 i2c 的外置 eeprom
野火指南者开发板上自带了一块 eeprom, 大小为 2Kb(2048bit), 也就是 256B(字节) 但是官方给的资料太少了.
这里还需要找到具体的datasheet把需要的参数挖掘出来.
根据以下参考资料
- https://doc.embedfire.com/mcu/stm32/f103zhinanzhe/std/zh/latest/book/I2C.html#id30
- https://blog.csdn.net/as480133937/article/details/105472659
- https://ww1.microchip.com/downloads/en/devicedoc/doc0180.pdf
从上面的数据, 挖掘到以下信息
- Internally Organized 256 x 8 (2K)
- 8-byte Page (1K, 2K) Write Modes
MCU 和 EEPROM 的连线上, 从野火开发板的wiki 上明确提到了, 用到 I2C1 接口, 引脚信息如下
- SCL PB6
- SDA PB7
这个两个引脚就不要接其他功能了, 专门用来往 eeprom 芯片里写入数据.
除了留出引脚, i2c 的写入还依赖时钟信号.
F10x 中 TIM3 TIM4 似乎会同时开关, 这个我还没完全确认
关于 eeprom 的处理代码, 涉及代码文件如下
- platforms/chibios/drivers/i2c_master.c i2s 驱动代码, 比如引脚初始化
- drivers/eeprom/eeprom_i2c.c eeprom 读写 i2c 接口的代码
- drivers/eeprom/eeprom_i2c.h eeprom 所有可用的配置选项, 需要在 config.h 或者 halconf 中配置
关于 eeprom 的地址大小, eeprom._i2c.h 中就提到, 256B 及以下, 应该选 1. 因此, 需要仔细阅读数据手册和头文件里的注释.
The address size in bytes of the EEPROM. For EEPROMs with <=256 bytes, this
will likely be 1. For EEPROMs >256 and <=65536, this will be 2. For EEPROMs65536, this will likely need to be 2 with the modified variant of
EXTERNAL_EEPROM_I2C_ADDRESS above.
高级特性默认都是关闭状态, 在 halconf 中需要一个一个开启, 包括后面的 PWM 和 DMA
1 |
|
接下来在 mcu 中声明 stm32 相关的宏变量.
1 |
|
halconf 优先级比较高, 可以提前定义模版变量.
mcuconf 先导入上一级的, 这里就得释放变量再重新定义
最后 在 confg.h 配置 eeprom 的核心参数:
1 |
我就简单加一行测试代码来输出 eeprom 是否启用成功来输出
1 |
|
另外, 还需要在 keyboard.json
中, 把 console 特性开启了, 这样子qmk会通过 usb HID 接口输出日志信息.
1 | "features": { |
更新完固件之后, 运行 qmk console
, 通过按下 A 键就可以看看 eeprom 的初始化是否正常了.
基于 flash 模拟的软 eeprom
除了使用专门的 eeprom 硬件, 更常用的是用 mcu 自己的flash 来存储配置. 虽然速度慢一点, 同时有寿命问题, 但是实现简单.
rules.mk 中加入
1 | EEPROM_DRIVER = vendor |
或者
1 | { |
qmk 已经内置了 stm32f1 和 stm32f4 的 eeprom 模拟实现. 一个配置就搞定了. 而且支持 wearleveling 均衡磨损算法, 可以有效保护 flash寿命.
Wear-Leveling,中文译名为均衡磨损,是一种算法,用于保护固态硬盘(SSD)中的存储单元,防止其因过度使用而损坏。SSD的存储单元,特别是NAND Flash,有固定的擦写次数限制。如果对同一个存储单元反复进行擦写操作,它将更快地达到其寿命极限。
Wear-Leveling算法的工作原理是将擦除和写入操作平均地分配到SSD的所有存储单元中,而不是集中在少数几个单元上。这样,所有存储单元的磨损程度就更加均衡,从而延长了SSD的整体寿命。
这种算法通过管理空闲块(未使用的存储单元)和数据块(已使用的存储单元)之间的转换来实现。当某个数据块被频繁擦写时,Wear-Leveling算法会将该块标记为空闲块,并在其他空闲块中选择一个块来存储数据。这样,原本频繁擦写的数据块就能得到休息,而其他空闲块则承担了更多的擦写操作。
光污染 RGB 键盘矩阵
最简单的 bitbang 驱动方式
在不少硬件项目中,特别在那些没有专用硬件序列化接口的情况下,bitbang 是一种可行的方案来驱动 RGB LED。
Bit-banging 是一种软件模拟的串行通信技术,可以在没有专用序列化硬件支持的情况下实现。这通常意味着通过快速切换 GPIO 的高低状态(电位),来模拟需要的时序脉冲。这种手动翻转通信协议的一个典型例子是 WS2812B LED 的数据线控制。由于 WS2812B 要求非常精确的时序来区分逻辑“0”和逻辑“1”,对代码的执行效率有较高的要求。
在 qmk 中启用 bitbang 驱动是非常简单的一件事情.
这里只简单开启两种动画作为测试.
1 | "rgb_matrix": { |
你也可以在 rules.mk 中开启
ws2812
的驱动方式为bitbang
title:rules.mk
1 WS2812_DRIVER = bitbang以及在
config.h
中配置需要 pin
1 #define WS2812_DI_PIN B0
尽管 bitbang 是一种通用方法,但高速 GPIO 切换会占用 MCU 大量资源,并且在多任务环境下可能导致时序问题。为了优化性能,可以考虑将数据发送和其他程序任务在时间上错开,减少资源争用的机会。此外,减小要控制的 LED 数量或降低更新频率也能降低对 MCU 资源的需求。
bitbang 这里作为一种简单验证的方法进行介绍, 就不过多展开了, 首先确保 LED 连线和工作正常, 接下来会介绍更高效的方式驱动 RGB 矩阵.
这里再放一张最后成果图, 换上了 4x4 行列扫描的薄膜键盘,, 一个小键盘大工告成.
使用 DMA 和 PWM 实现 rgb 会更节省资源, 但篇幅比较大, 待续