摘要:本文的目的,就在于通过【说明&分析】其功能+软硬件原理,提供一个尽可能达到“保姆级”的教程。当然,如果你有一定的开发板应用基础,甚至你还可以在此基础上进行“魔改”!
事情是这样的,为了方便学生能低成本【学习】【入门】开发板的应用,我做了一个简易的小键盘。
在制作过程中, 我发现其实键盘的原理也可以很好懂,人人都可以参考一个完整的开源文档,尝试自己DIY一个键盘。
本文的目的,就在于通过【说明&分析】其功能+软硬件原理,提供一个尽可能达到“保姆级”的教程。当然,如果你有一定的开发板应用基础,甚至你还可以在此基础上进行“魔改”!
一定很有趣!我已经迫不及待了!保姆级教程下滑查看!
提前说明:该键盘的成本可以压缩到50元,且已经全开源!
手搓了一个小键盘!
当你需要算一算618折扣的时候,它又可以摇身一变……秒变计算器!
可能你要说, 哎呀~我不喜欢绿光~我今天的心情是红色~可以的!因为它还……支持调光!
那么问题就来了!
这些功能之间如何切换?
——键盘可根据连接的设备自动切换功能!
原理图
PCB图
BOM表
可直接参考BOM放置器件哦!
①硬件参数说明
主控:使用立创·ColorEasyPico2 RP2350开发板,这里只引出了用到的几个接口,你可以根据需求引出需要的IO屏幕:SSD1306驱动0.91寸LCD 4Pin接口屏幕旋钮:EC11旋钮,为方便焊接,没有加入硬件防抖,全部通过软件处理抖动,降低焊接难度及成本热插拔轴座:使用支持热插拔的轴座,可自行配置你喜欢的键盘轴体RGB灯组:设计了16个RGB组成的矩阵为一组RGB,EC11右侧的独立按键为另外一组RGB,当然你也可以全部串联到一起节省一个IO口②供电电路设计说明
依照WS2812的数据手册要求,供电电压在3.5-5V电压之间。开发板上为3.3V和5V电压,为确保稳定性这里选择了5V供电。
但,依照WS2812数据手册中对信号电压的要求是+0.5VDD到-0.23VDD,而pico2开发板原理图中IO电压为3.3V,理论上还要增加一个电平转换电路。
不过在实际测试中WS2812的信号电压在2.4V以上时就被判断为1了,也就是说,逻辑判断电压实际和VDD电压影响不大,所以为了简化电路降低成本,选择不做电平转换直接连接。
二、软件设计本章主要分享——键盘、计算器、调光等功能是如何实现的?并附上源码。
①设计说明
RP2350具有双核Arm Cortex-M33核心和双核RISC-V Hazard3核心,可自由选择gcc编译,在这个项目中我们提供的两种固件,分别是:
Arm Cortex-M33 300Mhz超频固件RISC-V Hazard3 默频固件开发环境
软件环境:VSCode+PlatformIO开发语言:C/C++关于USB HID协议
在本项目中我们主要使用的是USB HID协议,在开发之前建议先认真阅读USB组织提供的USB HID协议文档
通过以下库完成本项目开发
TinyUSB Library:用于USB协议收发NeoPixel:用于RGB驱动GFX Library:用于图形绘制SSD1306:用于屏幕驱动通过以下函数完成小键盘逻辑处理
uint32_t Wheel(byte WheelPos) :彩虹色轮生成函数void effectRAINBOW:彩虹灯效void effectMarquee:跑马灯效void runLEDEffect:灯效调用void handleEncoderRotation:EC11旋钮逻辑判断float evaluateExpression(String expr):计算器逻辑处理②色轮生成函数
先将整个255色环被均分为三个区段:红->蓝(0-84) 2. 蓝->绿(85-169) 3. 绿->红(170-255)每个区段通过两个颜色分量的线性变化实现平滑过渡使用255色阶(3*85=255)确保颜色变化连贯无断层uint32_t Wheel(byte WheelPos) {// 反转输入值,使颜色轮逆向旋转(255为起点,0为终点)WheelPos = 255 - WheelPos;/* 第一阶段:红色 -> 蓝色渐变(当WheelPos在0-84范围时)if (WheelPos 绿色渐变(当WheelPos在85-169范围时)if (WheelPos 红色渐变(当WheelPos在170-255范围时)WheelPos -= 170; return strip.Color( WheelPos * 3, 255 - WheelPos * 3, 0 );}③灯效显示
这里直接用NeoPixel的setPixelColor函数指定RGB并显示就好了
void effectRainbow:彩虹渐变灯效void effectMarquee:红绿蓝跑马灯灯效void runLEDEffect:灯效模式调用函数void effectRainbow{static uint8_t offset = 0;for (int i = 0; i 200){ effectTimer = millis; strip.clear; strip.setPixelColor(marqueePosition, 0xFF0000); // 红色光点 strip.setPixelColor((marqueePosition + 4) % NUM_LEDS, 0x00FF00); // 绿色跟随 strip.setPixelColor((marqueePosition + 8) % NUM_LEDS, 0x0000FF); // 蓝色跟随 strip.show; marqueePosition = (marqueePosition + 1) % NUM_LEDS;}}// 灯效模式调用void runLEDEffect{switch (ledMode){case MODE_RAINBOW: effectRainbow; break;case MODE_MARQUEE: effectMarquee; break;}}④EC11旋钮逻辑判断
在旋钮逻辑判断函数里,这里对旋钮旋转方向的判断是通过EC11的CLK和DT两条信号线判断,这里我们可以先看一下旋钮旋转时的时序图
把这两个引脚状态合并成二进制来看,也就是说00 -> 01 -> 11 -> 10 -> 00为顺时针旋转,00 -> 10 -> 11 -> 01 -> 00为逆时针旋转,通过两两之间的变化就可以知道EC11旋钮是顺时针旋转还是逆时针旋转了
void handleEncoderRotation{// 静态变量保存前次状态(CLK和DT的组合状态)static uint8_t lastState = 0; // 存储上一次的引脚组合状态(2位二进制)static unsigned long lastTime = 0; // 存储上次状态变化的时间戳(防抖用)const unsigned long debounceDelay = 5; // 消抖时间阈值(单位:毫秒)// 读取当前编码器状态(将两个引脚状态合并为2位二进制数)// 格式:高1位是CLK引脚状态,低1位是DT引脚状态uint8_t state = (digitalRead(EC11_CLK) debounceDelay)){ /* 顺时针旋转状态序列检测(EC11编码器四步变化规律): * 00 -> 01 -> 11 -> 10 -> 00 * 当检测到以下任一有效转换时判定为顺时针旋转:*/ if ((lastState == 0b00 && state == 0b01) || (lastState == 0b01 && state == 0b11) || (lastState == 0b11 && state == 0b10) || (lastState == 0b10 && state == 0b00)) { // 顺时针旋转:降低目标亮度(需确保不低于最小值) targetBrightness = (targetBrightness - BRIGHTNESS_STEP); } /* 逆时针旋转状态序列检测(反向四步变化规律): * 00 -> 10 -> 11 -> 01 -> 00 * 当检测到以下任一有效转换时判定为逆时针旋转:*/ else if ( (lastState == 0b00 && state == 0b10) || (lastState == 0b10 && state == 0b11) || (lastState == 0b11 && state == 0b01) || (lastState == 0b01 && state == 0b00)) { // 逆时针旋转:增加目标亮度(需确保不超过最大值) targetBrightness = (targetBrightness + BRIGHTNESS_STEP); } // 更新状态变化时间戳 lastTime = millis; // 保存当前状态用于下次比较 lastState = state;}}⑤计算器逻辑
计算器这里则是通过按键拼接字符串,然后再解析字符串计算实现的,这里的函数主要是解析字符串
if (isdigit(c) || c == '.' || (c == '-' && newNum)):数字解析,解析正负数及小数else if (!newNum):运算符处理,解析加减乘除if (c == '+' || c == '-'):运算符类型判断,确保先乘除后加减if (numStr.length > 0):处理表达式末尾未被运算符触发的剩余数字float evaluateExpression(String expr){expr.replace(" ", ""); // 移除所有空格,确保表达式紧凑float result = 0; // 最终计算结果float currentTerm = 0; // 当前处理的运算项(用于处理乘除优先级)char op = '+'; // 当前运算符(初始化为+)String numStr; // 临时存储数字字符串(支持多位数和小数)bool newNum = true; // 标志位,表示是否开始新数字输入// 逐字符解析表达式for (int i = 0; i 0){ float num = numStr.toFloat; switch (op) // 根据最后的运算符处理 { case '+': currentTerm = num; break; case '-': currentTerm = -num; break; case 'x': currentTerm *= num; break; case '/': if (num == 0) return NAN; currentTerm /= num; break; }}result += currentTerm; // 将最后的当前项加入结果return result; // 返回最终计算结果}项目采用的是三段式结构,由前盖、PCB主板、后盖构成。
第一步:安装屏幕
首先将屏幕插入进前盖屏幕限位槽中
第二步:安装主板
将拼装完成的主板倒扣,主板屏幕排母对准屏幕排针插入
后盖倒扣插入即可
大佬魔改拓展方向
你可以在本项目的基础上添加更多灯效,屏幕功能~
因为RP2350带有USB串口通讯,你可以借助UART通讯使用QT/Flutter为小键盘开发一个上位机,而且主板还有余2个IO,1个可共用的IIC……完全可以实现更多自定义功能!
比如音乐律动灯效、电脑性能显示、上位机软件控制、自定义快捷键、温湿度监测等。
开源网址:https://oshwhub.com/course-examples/keyboard
【正文完】
来源:嘉立创EDA