Adding Pinecilv2 ws2812b mod option (#2099)

* support for WS2812B mod on Pinecil v2

* document support for WS2812B mod on Pinecil v2

* update IronOS-mkdocs.yml

* Protect WS2812B_Pin with define WS2812B_ENABLE
This commit is contained in:
Philippe Teuwen
2025-03-06 20:36:01 +01:00
committed by GitHub
parent 5e8ab27958
commit 83aa1b6425
9 changed files with 190 additions and 4 deletions

View File

@@ -28,6 +28,7 @@
- [Known Hardware Issues](../Documentation/HardwareIssues.md)
- [Power sources](../Documentation/PowerSources.md)
- [New Hardware Requirements](../Documentation/PortingToNewDevice.md)
- [WS2812B RGB Modding (Pinecil V2)](../Documentation/WS2812BModding.md)
- [Translations](../Documentation/Translation.md)
- [Development](../Documentation/Development.md)
- [Changelog](../Documentation/History.md)

View File

@@ -0,0 +1,33 @@
# WS2812B RGB Modding (Pinecil V2)
## What is it?
The idea of this mod is to bring the RGB feature of the MHP30 to the Pinecil V2.
Use a transparent shell for a better effect.
Pinecil V2 has a free GPIO_12 accessible through TP10, which is along the screen, cf [Pinecil PCB placement v2.0](https://files.pine64.org/doc/Pinecil/Pinecil_PCB_placement_v2.0_20220608.pdf) page 3. (TP9 (GPIO_14) is also available but hidden below the screen. If you want to use it, change `WS2812B_Pin` in `source/Core/BSP/Pinecilv2/Pins.h`.)
We'll using it to drive a WS2812B and let the color logic already present for the MHP30 do its magic:
- green when temperature is safe (< 55°C)
- pulsing red when heating
- solid red when desired temperature is reached
- orange when cooling down
## Electrical considerations
WS2812B requires a Vdd between 3.5 and 5.3V and Vih (high level of input signal) must be at least 0.7*Vdd.
Pinecil V2 GPIO levels are 3.3V and the 5V rail is actually max 4.6V.
So we can directly power the WS2812B on the 5V rail and command it with the GPIO without need for a level shifter, or for a Zener diode to clamp Vdd.
## How to wire it?
- WS2812B pin 1 (Vdd) is connected to the "5V" rail, e.g. on the C8 capacitor as illustrated [here](https://github.com/Ralim/IronOS/issues/1410#issuecomment-1296064392).
- WS2812B pin 3 (Vss) is connected to the Pinecil GND, e.g. on the U10 pad at the back of the PCB, below R35, as illustrated [here](https://github.com/Ralim/IronOS/issues/1410#issuecomment-1296064392).
- WS2812B pin 4 (Din) is connected to TP10.
You can use e.g. 0.1-mm enameled wire and isolate connections with UV glue to avoid any shortcut.
## How to enable it in the code?
`make firmware-EN model=Pinecilv2 ws2812b_enable=1`

View File

@@ -42,6 +42,7 @@ nav:
- Known Hardware Issues: HardwareIssues.md
- Power sources: PowerSources.md
- New Hardware Requirements: PortingToNewDevice.md
- WS2812B RGB Modding (Pinecil V2): WS2812BModding.md
- Translations: Translation.md
- Development: Development.md
- Changelog: History.md

View File

@@ -7,6 +7,9 @@
#include "Pins.h"
#include "Settings.h"
#include "Setup.h"
#if defined(WS2812B_ENABLE)
#include "WS2812B.h"
#endif
#include "TipThermoModel.h"
#include "USBPD.h"
#include "Utils.hpp"
@@ -27,6 +30,10 @@ uint8_t tempMeasureTicks = 25;
uint16_t totalPWM = 255; // Total length of the cycle's ticks
#if defined(WS2812B_ENABLE)
WS2812B<WS2812B_Pin, 1> ws2812b;
#endif
void resetWatchdog() {
// #TODO
}
@@ -124,6 +131,12 @@ uint8_t getButtonB() {
return val;
}
void BSPInit(void) {
#if defined(WS2812B_ENABLE)
ws2812b.init();
#endif
}
void reboot() { hal_system_reset(); }
void delay_ms(uint16_t count) {
@@ -143,7 +156,33 @@ bool isTipDisconnected() {
}
void setStatusLED(const enum StatusLED state) {
// Dont have one
#if defined(WS2812B_ENABLE)
static enum StatusLED lastState = LED_UNKNOWN;
if (lastState != state || state == LED_HEATING) {
switch (state) {
default:
case LED_UNKNOWN:
case LED_OFF:
ws2812b.led_set_color(0, 0, 0, 0);
break;
case LED_STANDBY:
ws2812b.led_set_color(0, 0, 0xFF, 0); // green
break;
case LED_HEATING: {
ws2812b.led_set_color(0, ((xTaskGetTickCount() / 4) % 192) + 64, 0, 0); // Red fade
} break;
case LED_HOT:
ws2812b.led_set_color(0, 0xFF, 0, 0); // red
break;
case LED_COOLING_STILL_HOT:
ws2812b.led_set_color(0, 0xFF, 0x20, 0x00); // Orange
break;
}
ws2812b.led_update();
lastState = state;
}
#endif
}
void setBuzzer(bool on) {}

View File

@@ -41,4 +41,11 @@
#define UART_TX_Pin GPIO_PIN_22
#define UART_RX_Pin GPIO_PIN_23
#if defined(WS2812B_ENABLE)
// WS2812B mod using TP10
#define WS2812B_Pin GPIO_PIN_12
// WS2812B mod using TP9 is doable too, but harder to reach. Thanks @t3chguy
//#define WS2812B_Pin GPIO_PIN_14
#endif
#endif /* BSP_PINE64_PINS_H_ */

View File

@@ -20,5 +20,6 @@ void preRToSInit() {
gpio_write(OLED_RESET_Pin, 0);
delay_ms(10);
gpio_write(OLED_RESET_Pin, 1);
BSPInit();
FRToSI2C::FRToSInit();
}

View File

@@ -0,0 +1,81 @@
/*
* WS2812B.h
*
* Created on: 9 July 2023
* Author: Doegox
* Currently for RISC-V architecture only
* Based on WS2812.h by Ralim for STM32
*/
#include "Pins.h"
#include "Setup.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifndef CORE_DRIVERS_WS2812B_H_
#define CORE_DRIVERS_WS2812B_H_
#ifndef WS2812B_LED_CHANNEL_COUNT
#define WS2812B_LED_CHANNEL_COUNT 3
#endif
#define WS2812B_RAW_BYTES_PER_LED (WS2812B_LED_CHANNEL_COUNT * 8)
template <uint16_t LED_PIN, int LED_COUNT> class WS2812B {
private:
uint8_t leds_colors[WS2812B_LED_CHANNEL_COUNT * LED_COUNT];
public:
void led_update() {
__disable_irq();
// Bitbang it out as our cpu irq latency is too high
for (unsigned int i = 0; i < sizeof(leds_colors); i++) {
// Shove out MSB first
for (int x = 0; x < 8; x++) {
if ((leds_colors[i] & (1 << (7 - x))) == (1 << (7 - x))) {
gpio_write(LED_PIN, 1);
for (int k = 0; k < 27; k++) {
__ASM volatile("nop");
}
gpio_write(LED_PIN, 0);
for (int k = 0; k < 10; k++) {
__ASM volatile("nop");
}
} else {
gpio_write(LED_PIN, 1);
for (int k = 0; k < 10; k++) {
__ASM volatile("nop");
}
gpio_write(LED_PIN, 0);
for (int k = 0; k < 27; k++) {
__ASM volatile("nop");
}
}
}
}
__enable_irq();
}
void init(void) { memset(leds_colors, 0, sizeof(leds_colors));
gpio_set_mode(LED_PIN, GPIO_OUTPUT_MODE);
gpio_write(LED_PIN, 1);
led_set_color(0, 0, 0xFF, 0); // green
led_update();
}
void led_set_color(size_t index, uint8_t r, uint8_t g, uint8_t b) {
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 0] = g;
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 1] = r;
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 2] = b;
}
void led_set_color_all(uint8_t r, uint8_t g, uint8_t b) {
for (int index = 0; index < LED_COUNT; index++) {
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 0] = g;
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 1] = r;
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 2] = b;
}
}
};
#endif /* CORE_DRIVERS_WS2812B_H_ */

View File

@@ -361,6 +361,12 @@ ifdef swd_enable
GLOBAL_DEFINES+=-DSWD_ENABLE
endif
ifeq ($(model),$(filter $(model),$(ALL_PINECIL_V2_MODELS)))
ifdef ws2812b_enable
GLOBAL_DEFINES += -DWS2812B_ENABLE
endif
endif
# Libs -------------------------------------------------------------------------
LIBS=

View File

@@ -8,6 +8,7 @@ AVAILABLE_LANGUAGES=()
BUILD_LANGUAGES=()
AVAILABLE_MODELS=("TS100" "TS80" "TS80P" "Pinecil" "MHP30" "Pinecilv2" "S60" "S60P" "T55" "TS101")
BUILD_MODELS=()
OPTIONS=()
builder_info() {
echo -e "
@@ -28,17 +29,19 @@ usage() {
builder_info
echo -e "
Usage :
$(basename "$0") [-l <LANG_CODES>] [-m <MODELS>] [-h]
$(basename "$0") [-l <LANG_CODES>] [-m <MODELS>] [-o <OPTIONS>] [-h]
Parameters :
-l LANG_CODE : Force a specific language (${AVAILABLE_LANGUAGES[*]})
-m MODEL : Force a specific model (${AVAILABLE_MODELS[*]})
-o key=val : Pass options to make
-h : Show this help message
Example :
$(basename "$0") -l EN -m TS100 (Build one language and model)
$(basename "$0") -l EN -m \"TS100 MHP30\" (Build one language and multi models)
$(basename "$0") -l \"DE EN\" -m \"TS100 MHP30\" (Build multi languages and models)
$(basename "$0") -l EN -m Pinecilv2 -o ws2812b_enable=1
INFO :
By default, without parameters, the build is for all platforms and all languages
@@ -84,8 +87,9 @@ isInArray() {
declare -a margs=()
declare -a largs=()
declare -a oargs=()
while getopts "h:l:m:" option; do
while getopts "h:l:m:o:" option; do
case "${option}" in
h)
usage
@@ -96,6 +100,9 @@ while getopts "h:l:m:" option; do
m)
IFS=' ' read -r -a margs <<<"${OPTARG}"
;;
o)
IFS=' ' read -r -a oargs <<< "${OPTARG}"
;;
*)
usage
;;
@@ -156,6 +163,16 @@ fi
echo "********************************************"
echo -n "Requested options : "
if ((${#oargs[@]})); then
for i in "${oargs[@]}"; do
echo -n "$i "
OPTIONS+=("$i")
done
echo ""
fi
echo "********************************************"
##
#StartBuild
@@ -168,7 +185,7 @@ if [ ${#BUILD_LANGUAGES[@]} -gt 0 ] && [ ${#BUILD_MODELS[@]} -gt 0 ]; then
for model in "${BUILD_MODELS[@]}"; do
echo "Building firmware for $model in ${BUILD_LANGUAGES[*]}"
make -j"$(nproc)" model="$model" "${BUILD_LANGUAGES[@]/#/firmware-}" >/dev/null
make -j"$(nproc)" model="$model" "${BUILD_LANGUAGES[@]/#/firmware-}" "${OPTIONS[@]}" >/dev/null
checkLastCommand
done
else