Skip to content

Commit 83aa1b6

Browse files
authored
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
1 parent 5e8ab27 commit 83aa1b6

File tree

9 files changed

+190
-4
lines changed

9 files changed

+190
-4
lines changed

Documentation/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [Known Hardware Issues](../Documentation/HardwareIssues.md)
2929
- [Power sources](../Documentation/PowerSources.md)
3030
- [New Hardware Requirements](../Documentation/PortingToNewDevice.md)
31+
- [WS2812B RGB Modding (Pinecil V2)](../Documentation/WS2812BModding.md)
3132
- [Translations](../Documentation/Translation.md)
3233
- [Development](../Documentation/Development.md)
3334
- [Changelog](../Documentation/History.md)

Documentation/WS2812BModding.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# WS2812B RGB Modding (Pinecil V2)
2+
3+
## What is it?
4+
5+
The idea of this mod is to bring the RGB feature of the MHP30 to the Pinecil V2.
6+
Use a transparent shell for a better effect.
7+
8+
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`.)
9+
10+
We'll using it to drive a WS2812B and let the color logic already present for the MHP30 do its magic:
11+
12+
- green when temperature is safe (< 55°C)
13+
- pulsing red when heating
14+
- solid red when desired temperature is reached
15+
- orange when cooling down
16+
17+
## Electrical considerations
18+
19+
WS2812B requires a Vdd between 3.5 and 5.3V and Vih (high level of input signal) must be at least 0.7*Vdd.
20+
Pinecil V2 GPIO levels are 3.3V and the 5V rail is actually max 4.6V.
21+
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.
22+
23+
## How to wire it?
24+
25+
- 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).
26+
- 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).
27+
- WS2812B pin 4 (Din) is connected to TP10.
28+
29+
You can use e.g. 0.1-mm enameled wire and isolate connections with UV glue to avoid any shortcut.
30+
31+
## How to enable it in the code?
32+
33+
`make firmware-EN model=Pinecilv2 ws2812b_enable=1`

scripts/IronOS-mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ nav:
4242
- Known Hardware Issues: HardwareIssues.md
4343
- Power sources: PowerSources.md
4444
- New Hardware Requirements: PortingToNewDevice.md
45+
- WS2812B RGB Modding (Pinecil V2): WS2812BModding.md
4546
- Translations: Translation.md
4647
- Development: Development.md
4748
- Changelog: History.md

source/Core/BSP/Pinecilv2/BSP.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include "Pins.h"
88
#include "Settings.h"
99
#include "Setup.h"
10+
#if defined(WS2812B_ENABLE)
11+
#include "WS2812B.h"
12+
#endif
1013
#include "TipThermoModel.h"
1114
#include "USBPD.h"
1215
#include "Utils.hpp"
@@ -27,6 +30,10 @@ uint8_t tempMeasureTicks = 25;
2730

2831
uint16_t totalPWM = 255; // Total length of the cycle's ticks
2932

33+
#if defined(WS2812B_ENABLE)
34+
WS2812B<WS2812B_Pin, 1> ws2812b;
35+
#endif
36+
3037
void resetWatchdog() {
3138
// #TODO
3239
}
@@ -124,6 +131,12 @@ uint8_t getButtonB() {
124131
return val;
125132
}
126133

134+
void BSPInit(void) {
135+
#if defined(WS2812B_ENABLE)
136+
ws2812b.init();
137+
#endif
138+
}
139+
127140
void reboot() { hal_system_reset(); }
128141

129142
void delay_ms(uint16_t count) {
@@ -143,7 +156,33 @@ bool isTipDisconnected() {
143156
}
144157

145158
void setStatusLED(const enum StatusLED state) {
146-
// Dont have one
159+
#if defined(WS2812B_ENABLE)
160+
static enum StatusLED lastState = LED_UNKNOWN;
161+
162+
if (lastState != state || state == LED_HEATING) {
163+
switch (state) {
164+
default:
165+
case LED_UNKNOWN:
166+
case LED_OFF:
167+
ws2812b.led_set_color(0, 0, 0, 0);
168+
break;
169+
case LED_STANDBY:
170+
ws2812b.led_set_color(0, 0, 0xFF, 0); // green
171+
break;
172+
case LED_HEATING: {
173+
ws2812b.led_set_color(0, ((xTaskGetTickCount() / 4) % 192) + 64, 0, 0); // Red fade
174+
} break;
175+
case LED_HOT:
176+
ws2812b.led_set_color(0, 0xFF, 0, 0); // red
177+
break;
178+
case LED_COOLING_STILL_HOT:
179+
ws2812b.led_set_color(0, 0xFF, 0x20, 0x00); // Orange
180+
break;
181+
}
182+
ws2812b.led_update();
183+
lastState = state;
184+
}
185+
#endif
147186
}
148187
void setBuzzer(bool on) {}
149188

source/Core/BSP/Pinecilv2/Pins.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,11 @@
4141
#define UART_TX_Pin GPIO_PIN_22
4242
#define UART_RX_Pin GPIO_PIN_23
4343

44+
#if defined(WS2812B_ENABLE)
45+
// WS2812B mod using TP10
46+
#define WS2812B_Pin GPIO_PIN_12
47+
// WS2812B mod using TP9 is doable too, but harder to reach. Thanks @t3chguy
48+
//#define WS2812B_Pin GPIO_PIN_14
49+
#endif
50+
4451
#endif /* BSP_PINE64_PINS_H_ */

source/Core/BSP/Pinecilv2/preRTOS.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ void preRToSInit() {
2020
gpio_write(OLED_RESET_Pin, 0);
2121
delay_ms(10);
2222
gpio_write(OLED_RESET_Pin, 1);
23+
BSPInit();
2324
FRToSI2C::FRToSInit();
2425
}

source/Core/Drivers/WS2812B.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* WS2812B.h
3+
*
4+
* Created on: 9 July 2023
5+
* Author: Doegox
6+
* Currently for RISC-V architecture only
7+
* Based on WS2812.h by Ralim for STM32
8+
*/
9+
#include "Pins.h"
10+
#include "Setup.h"
11+
#include <stddef.h>
12+
#include <stdint.h>
13+
#include <string.h>
14+
15+
#ifndef CORE_DRIVERS_WS2812B_H_
16+
#define CORE_DRIVERS_WS2812B_H_
17+
18+
#ifndef WS2812B_LED_CHANNEL_COUNT
19+
#define WS2812B_LED_CHANNEL_COUNT 3
20+
#endif
21+
22+
#define WS2812B_RAW_BYTES_PER_LED (WS2812B_LED_CHANNEL_COUNT * 8)
23+
24+
template <uint16_t LED_PIN, int LED_COUNT> class WS2812B {
25+
private:
26+
uint8_t leds_colors[WS2812B_LED_CHANNEL_COUNT * LED_COUNT];
27+
28+
public:
29+
void led_update() {
30+
__disable_irq();
31+
// Bitbang it out as our cpu irq latency is too high
32+
for (unsigned int i = 0; i < sizeof(leds_colors); i++) {
33+
// Shove out MSB first
34+
for (int x = 0; x < 8; x++) {
35+
if ((leds_colors[i] & (1 << (7 - x))) == (1 << (7 - x))) {
36+
gpio_write(LED_PIN, 1);
37+
for (int k = 0; k < 27; k++) {
38+
__ASM volatile("nop");
39+
}
40+
gpio_write(LED_PIN, 0);
41+
for (int k = 0; k < 10; k++) {
42+
__ASM volatile("nop");
43+
}
44+
} else {
45+
gpio_write(LED_PIN, 1);
46+
for (int k = 0; k < 10; k++) {
47+
__ASM volatile("nop");
48+
}
49+
gpio_write(LED_PIN, 0);
50+
for (int k = 0; k < 27; k++) {
51+
__ASM volatile("nop");
52+
}
53+
}
54+
}
55+
}
56+
__enable_irq();
57+
}
58+
59+
void init(void) { memset(leds_colors, 0, sizeof(leds_colors));
60+
gpio_set_mode(LED_PIN, GPIO_OUTPUT_MODE);
61+
gpio_write(LED_PIN, 1);
62+
led_set_color(0, 0, 0xFF, 0); // green
63+
led_update();
64+
}
65+
66+
void led_set_color(size_t index, uint8_t r, uint8_t g, uint8_t b) {
67+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 0] = g;
68+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 1] = r;
69+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 2] = b;
70+
}
71+
72+
void led_set_color_all(uint8_t r, uint8_t g, uint8_t b) {
73+
for (int index = 0; index < LED_COUNT; index++) {
74+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 0] = g;
75+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 1] = r;
76+
leds_colors[index * WS2812B_LED_CHANNEL_COUNT + 2] = b;
77+
}
78+
}
79+
};
80+
81+
#endif /* CORE_DRIVERS_WS2812B_H_ */

source/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ ifdef swd_enable
361361
GLOBAL_DEFINES+=-DSWD_ENABLE
362362
endif
363363

364+
ifeq ($(model),$(filter $(model),$(ALL_PINECIL_V2_MODELS)))
365+
ifdef ws2812b_enable
366+
GLOBAL_DEFINES += -DWS2812B_ENABLE
367+
endif
368+
endif
369+
364370
# Libs -------------------------------------------------------------------------
365371
LIBS=
366372

source/build.sh

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ AVAILABLE_LANGUAGES=()
88
BUILD_LANGUAGES=()
99
AVAILABLE_MODELS=("TS100" "TS80" "TS80P" "Pinecil" "MHP30" "Pinecilv2" "S60" "S60P" "T55" "TS101")
1010
BUILD_MODELS=()
11+
OPTIONS=()
1112

1213
builder_info() {
1314
echo -e "
@@ -28,17 +29,19 @@ usage() {
2829
builder_info
2930
echo -e "
3031
Usage :
31-
$(basename "$0") [-l <LANG_CODES>] [-m <MODELS>] [-h]
32+
$(basename "$0") [-l <LANG_CODES>] [-m <MODELS>] [-o <OPTIONS>] [-h]
3233
3334
Parameters :
3435
-l LANG_CODE : Force a specific language (${AVAILABLE_LANGUAGES[*]})
3536
-m MODEL : Force a specific model (${AVAILABLE_MODELS[*]})
37+
-o key=val : Pass options to make
3638
-h : Show this help message
3739
3840
Example :
3941
$(basename "$0") -l EN -m TS100 (Build one language and model)
4042
$(basename "$0") -l EN -m \"TS100 MHP30\" (Build one language and multi models)
4143
$(basename "$0") -l \"DE EN\" -m \"TS100 MHP30\" (Build multi languages and models)
44+
$(basename "$0") -l EN -m Pinecilv2 -o ws2812b_enable=1
4245
4346
INFO :
4447
By default, without parameters, the build is for all platforms and all languages
@@ -84,8 +87,9 @@ isInArray() {
8487

8588
declare -a margs=()
8689
declare -a largs=()
90+
declare -a oargs=()
8791

88-
while getopts "h:l:m:" option; do
92+
while getopts "h:l:m:o:" option; do
8993
case "${option}" in
9094
h)
9195
usage
@@ -96,6 +100,9 @@ while getopts "h:l:m:" option; do
96100
m)
97101
IFS=' ' read -r -a margs <<<"${OPTARG}"
98102
;;
103+
o)
104+
IFS=' ' read -r -a oargs <<< "${OPTARG}"
105+
;;
99106
*)
100107
usage
101108
;;
@@ -156,6 +163,16 @@ fi
156163

157164
echo "********************************************"
158165

166+
echo -n "Requested options : "
167+
if ((${#oargs[@]})); then
168+
for i in "${oargs[@]}"; do
169+
echo -n "$i "
170+
OPTIONS+=("$i")
171+
done
172+
echo ""
173+
fi
174+
175+
echo "********************************************"
159176
##
160177
#StartBuild
161178

@@ -168,7 +185,7 @@ if [ ${#BUILD_LANGUAGES[@]} -gt 0 ] && [ ${#BUILD_MODELS[@]} -gt 0 ]; then
168185

169186
for model in "${BUILD_MODELS[@]}"; do
170187
echo "Building firmware for $model in ${BUILD_LANGUAGES[*]}"
171-
make -j"$(nproc)" model="$model" "${BUILD_LANGUAGES[@]/#/firmware-}" >/dev/null
188+
make -j"$(nproc)" model="$model" "${BUILD_LANGUAGES[@]/#/firmware-}" "${OPTIONS[@]}" >/dev/null
172189
checkLastCommand
173190
done
174191
else

0 commit comments

Comments
 (0)