Skip to content

Commit ba894ef

Browse files
krisjdevurish
authored andcommitted
fix: add missing files causing failing build
From what I understand of the error, it's expecting translated versions of the newly written tutorial... unfortunately I don't know these languages so I've just commited copies of the English version to de-DE and pt-BR.
1 parent 305c56e commit ba894ef

File tree

2 files changed

+524
-0
lines changed

2 files changed

+524
-0
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
---
2+
title: 'Tutorial: 7-segment display'
3+
sidebar_label: 'Tutorial: 7-segement display'
4+
---
5+
6+
# Tutorial: 7-segment display
7+
8+
## Introduction
9+
The Custom Chips API allows you to create new simulation models and behaviors that extend the functionality of Wokwi.
10+
You can create new sensors, displays, memories, testing instruments and even simulate your own hardware.
11+
12+
Custom chips are usually written in C, and have an accompanying JSON file that describes the pinout, as well as any
13+
input values for the chip (e.g. the current temperature for a temperature sensor chip). Other languages are also
14+
available - more on that later.
15+
16+
In this tutorial we'll learn how to get started with the Chips API by implementing a simple 7-segment controller chip.
17+
The chip will get a character (0-9 or A-F) via UART interface, and will display it on a
18+
[7-segment display](https://docs.wokwi.com/parts/wokwi-7segment#using-the-7-segment-display).
19+
20+
Let's get started!
21+
22+
## The pinout
23+
Before we dive into the code, let's define the pinout for the chip:
24+
25+
| Name | Type | Function |
26+
| :- | :- | :- |
27+
| VCC | Power | Supply voltage |
28+
| GND | Power | Ground |
29+
| RX | Input | UART |
30+
| SEG_A | Output | 7-segment |
31+
| SEG_B | Output | 7-segment |
32+
| SEG_C | Output | 7-segment |
33+
| SEG_D | Output | 7-segment |
34+
| SEG_E | Output | 7-segment |
35+
| SEG_F | Output | 7-segment |
36+
| SEG_G | Output | 7-segment |
37+
38+
Our chip will have a total of 10 pins: two power supply pins, one UART input pin (RX), and 7 output pins to drive the
39+
7-segment display. For simplicity, we'll assume that the 7-segment display is a common anode display, which is the
40+
default on Wokwi.
41+
42+
## The chip JSON file
43+
Now we're ready to start writing code! We'll start from an empty ESP32-C3 project:
44+
[wokwi.com/projects/new/esp32-c3](https://wokwi.com/projects/new/esp32-c3).
45+
46+
The first thing we need to do is to create a custom chip. The easiest way to go about this is to press the blue "+"
47+
button and serach for "Custom Chip". After selecting this option, type "sevseg-controller" for the chip name. Select
48+
the "C" language option.
49+
50+
Wokwi will add a green breakout board for your custom chip, and create two new files in your project:
51+
- `sevseg-controller.chip.json` - defines the pinout
52+
- `sevseg-controller.chip.c` - defines the logic for the chip
53+
54+
We'll start by editing the `sevseg-controller.chip.json` as follows:
55+
1. Change the `name` of the chip to "7 Segment Controller"
56+
2. Change the `author` of the chip to your name
57+
3. Change the `pins` array of the chip to include all the pin names:
58+
```json
59+
["VCC", "GND", "RX", "SEG_A", "SEG_B", "SEG_C", "SEG_D", "SEG_E", "SEG_F", "SEG_G"]
60+
```
61+
62+
You'll see the green breakout board updating as you make changes to the JSON file.
63+
64+
## Implementing the chip's logic
65+
Next, go to `sevseg-controller.chip.c`. This file implements the chip logic. The two important parts are:
66+
1. The `chip_state_t` struct - use it to store all the state information of your chip, together with all the objects
67+
that you create for your chips: IO pins, timers, etc.
68+
2. The `chip_init` function - Wokwi will call this function for every instance (copy) of your chip. The function should
69+
allocate memory for a new `chip_state_t` struct, initialize all the IO pins, the chip's state, and create any
70+
relevant objects (we'll see an example in a minute).
71+
72+
The default implementation does not include any state or initialization - it only prints a message saying "Hello from
73+
custom chip!". You will see this message when you start the simulation - it'll appear in a new "Chips Console" tab
74+
below the diagram.
75+
76+
We'll modify `chip_init` to perform the following actions:
77+
1. Initialize all the `SEG_x` pins as outputs
78+
2. Listen for UART data on the `RX` pin, and call a function that will update the `SEG_x` according to the character we
79+
received.
80+
81+
### Initializing the 7-segment outputs
82+
Start by adding a `segment_pins` array to the `chip_state_t` struct. This array will store a reference to the `SEG_x`
83+
output pins, so we'll need to allocate 7 items:
84+
```C
85+
typedef struct {
86+
pin_t segment_pins[7];
87+
} chip_state_t;
88+
```
89+
90+
Next, add the following code to `chip_init` (after the line that defines `chip`):
91+
```C
92+
chip->segment_pins[0] = pin_init("SEG_A", OUTPUT_HIGH);
93+
chip->segment_pins[0] = pin_init("SEG_B", OUTPUT_HIGH);
94+
chip->segment_pins[0] = pin_init("SEG_C", OUTPUT_HIGH);
95+
chip->segment_pins[0] = pin_init("SEG_D", OUTPUT_HIGH);
96+
chip->segment_pins[0] = pin_init("SEG_E", OUTPUT_HIGH);
97+
chip->segment_pins[0] = pin_init("SEG_F", OUTPUT_HIGH);
98+
chip->segment_pins[0] = pin_init("SEG_G", OUTPUT_HIGH);
99+
```
100+
101+
The code initializes each of the segment pins as an output, and sets the initial value to digital high. The 7-segment
102+
display has a common annode, so setting a segment pin high will turn that segment off. You can learn more about the
103+
`pin_init` function in the [GPIO API reference](../chips-api/gpio.md).
104+
105+
### Listening to UART data
106+
Add the following code to `chip_init`, right after the code that initializes the segment pins:
107+
```C
108+
const uart_config_t uart_config = {
109+
.tx = NO_PIN,
110+
.rx = pin_init("RX", INPUT),
111+
.buad_rate = 115200,
112+
.rx_data = on_uart_rx_data,
113+
.user_data = chip,
114+
};
115+
uart_init(&uart_config);
116+
```
117+
118+
The code configures the `RX` pin as an input (in the third line), and sets up a `uart_config_t` structure. This
119+
structure configures the baud rate, as well as a function that will get called whenever there is new data,
120+
`on_uart_rx_data`.
121+
122+
Also note how we set `.user_data` to `chip` - this is important, as this value will be passed as a parameter to the
123+
`on_uart_rx_data` function, providing it access to our chip's state.
124+
125+
In our case, we are only interested in receiving data, so we set `.tx` to the special `NO_PIN` value.
126+
127+
:::tip
128+
129+
To learn more about using UART in Wokwi, check out the [UART API reference](../chips-api/uart.md).
130+
131+
:::
132+
133+
### From UART to 7-segment
134+
For the final part of the show, we'll implement the `on_uart_rx_data` callback. Paste the following code above the
135+
definition of `chip_init`:
136+
```C
137+
const uint8_t font[] = {
138+
['0'] = 0b11000000,
139+
['1'] = 0b11111001,
140+
['2'] = 0b10100100,
141+
['3'] = 0b10110000,
142+
['4'] = 0b10011001,
143+
['5'] = 0b10010010,
144+
['6'] = 0b10000010,
145+
['7'] = 0b11111000,
146+
['8'] = 0b10000000,
147+
['9'] = 0b10010000,
148+
['A'] = 0b10001000,
149+
['B'] = 0b10000011,
150+
['C'] = 0b11000110,
151+
['D'] = 0b10100001,
152+
['E'] = 0b10000110,
153+
['F'] = 0b10001110,
154+
};
155+
156+
static void on_uart_rx_data(void *user_data, uint8_t byte) {
157+
chip_state_t *chip = user_data;
158+
uint8_t font_char = font[byte];
159+
if (font_char) {
160+
for (int bit = 0; bit < 7; bit++) {
161+
uint8_t bit_value = font_char & (1 << bit);
162+
pin_write(chip->segment_pins[bit], bit_value ? HIGH : LOW);
163+
}
164+
}
165+
}
166+
```
167+
168+
This part simply defines the "font" - it maps between a character that we receive from UART and the corresponding
169+
segments that need to be turned on. Our 7-segment display has a common anode, so 0 will ligh a segment, and 1 will turn
170+
it off.
171+
172+
The `on_uart_rx_data` is where the actual magic happens. We use the `font` array to lookup the `byte` we received over
173+
UART. If we find a match (when `font_char` is not 0), we iterate over the bits of the `font_char`, and update each
174+
segment to its corresponding bit in `font_char`.
175+
176+
That's it - we created a simple 7-segment controller chip for Wokwi!
177+
178+
## Testing the chip
179+
You can test the chip by adding a 7-segment display to the diagram, and writing it to the chip. Don't forget to write
180+
the common pin of the 7-segment display to the 3.3V or 5V pin of the ESP32-C3 board!
181+
182+
Next, wire the `RX` pin of the chip to the `TX` pin of the ESP32-C3 board. You can also wire the GND/VCC pins of the
183+
chip for good measures, even though the chip will be functional even without these pins.
184+
185+
Finally, pase the following code into `sketch.ino`:
186+
```C
187+
void setup() {
188+
Serial.begin(115200);
189+
}
190+
191+
int i = 0;
192+
void loop() {
193+
Serial.println(i, HEX);
194+
i++;
195+
if (i > 0xf) {
196+
i = 0;
197+
}
198+
delay(500);
199+
}
200+
```
201+
202+
It's a simple program that outputs all the hexadecimal values between 0 and F to the ESP32-C3's serial port - all the
203+
characters that are included in our custom chip's font. If you prefer plain C code, you can change the first line in
204+
`loop` to use `printf()` instead.
205+
```C
206+
printf("%X\n", i);
207+
```
208+
209+
When you start the simulation, you should see the 7-segment display counting from 0 to F repeatedly. Hooray!
210+
211+
Doesn't work? No worries, here's the link to the final result, so you can compare it with yours:
212+
[wokwi.com/projects/371252876830114817](https://wokwi.com/projects/371252876830114817).
213+
214+
## Under the hood
215+
How do custom chips work? What happens with the C code you write? Behind the scenes, Wokwi takes this code and compiles
216+
it into a Web Assembly module using LLVM (if you are curious, here's the
217+
[Docker container](https://github.com/wokwi/wokwi-builders/blob/main/clang-wasm/Dockerfile) that does all the magic).
218+
219+
When you run the simulation, Wokwi creates an instance of the Web Assembly module, and calls `chip_init` once for
220+
every instance of the chip in your diagram.
221+
222+
Using Web Assembly means you can write your code in a variety of languages. Currently, only C is officially supported,
223+
there are some examples of how to write custom chips with Rust, AssemblyScript and even Zig.
224+
225+
There's even a hack where we use Custom Chips to simulate Verilog: we use Yosys CXXRTL to convert your Verilog code
226+
into C++, and then
227+
[use emscripten to compile](https://github.com/wokwi/wokwi-builders/blob/main/verilog-cxxrtl/project/compile.sh)
228+
the result along with [some glue code](https://github.com/wokwi/wokwi-builders/blob/main/verilog-cxxrtl/project/main.cpp)
229+
into Web Assembly. Scary cool?
230+
231+
## Next steps
232+
If you want to dive deeper into the Custom Chips API, here are some ideas how to build on the chip we created in this
233+
tutorial:
234+
235+
- **Fix the bug!** <br/>
236+
Unfortunately, our code has a bug - some values will cause it to display garbage on the 7-segment display (try sending
237+
it a 'b'). Some bound checking can help!
238+
239+
- **Add support for common cathode 7-segment displays** <br/>
240+
You can use an additional input pin to select between common anode/cathode, or use the [Attributes API](../chips-api/attributes.md)
241+
to allow the user to define the type of display by editing the chip attributes in `diagram.json`.
242+
243+
- **Add another communication protocol** <br/>
244+
You can turn our 7-segment display into an [I2C](../chips-api/i2c.md) or an [SPI](../chips-api/spi.md) device.
245+
246+
- **Support multiple digits** <br/>
247+
Control a two or four digital 7-segment display! Use the [Time API](../chips-api/time.md) to create a timer that will quickly
248+
alternate between the digits.
249+
250+
- **Add analog input** <br/>
251+
Use the [Analog API](../chips-api/analog.md) to read and display an analog input value. This makes the 7-segment controller chips
252+
useful even without a microcontroller - you can connect it directly to a potentiometer or an analog sensor, and
253+
display the reading directly.
254+
255+
- **Share your chip on GitHub** <br/>
256+
By sharing your chip's code on GitHub, you can make it easy for other users to include it in their project. You can
257+
use the [inverter-chip repo](https://github.com/wokwi/inverter-chip) as a starting point - it has a
258+
[GitHub action](https://github.com/wokwi/inverter-chip/blob/main/.github/workflows/build.yaml) that automatically
259+
compiles the chips and creates a release whenever you push a tag.
260+
261+
Here's an [example for a Wokwi project](https://wokwi.com/projects/350946636543820370) that uses this chip. Note the
262+
"dependencies" section in `diagram.json` - it tells Wokwi where to look for the chip implementation on GitHub.

0 commit comments

Comments
 (0)