Skip to content

Commit 0271218

Browse files
authored
Merge pull request #1 from gcoonrod/feature/board-rev2
Finished board rev1
2 parents 7f00100 + 4e5afaf commit 0271218

24 files changed

+868617
-74254
lines changed

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"cSpell.words": [
3+
"Supercap",
4+
"VBAT"
5+
]
6+
}

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,33 @@
22

33
A proof-of-concept STM32 based development board designed for experimentation with 74x595 driven 7-segment LED character displays.
44

5+
## Current Revision
6+
Sponsored by:
7+
8+
![PCBWay](./hardware/assets/PCBWay.png)
9+
10+
Due to a generous sponsorship from PCBWay the current hardware revision (rev 1) includes a number of substantial design changes over the initial revision:
11+
- All SMD components from rev 0 moved to the back.
12+
- Most THT components from rev 0 changed to hand-solder friendly SMD components.
13+
- Supercap backup for VBAT switched to more common CR2032 battery holder.
14+
- Addition of ESD IC for the USB connection and a correction for an issue with the USB FS series and pull-up resistors.
15+
- Move to a 4 layer PCB and use of via-in-pad resin plug with copper capping to simplify routing.
16+
- Addition of an aluminum sheet metal stand.
17+
- Remap latch OE pin and user LED pins to PWM capable outputs.
18+
- Break out UART3 "just in case".
19+
20+
### Revision Objectives
21+
Issues with the USB connection in the first revision (rev 0) made a second revision necessary in order to support UART communication over the USB connection. While the USB termination bug was the primary driver for spinning a new board, there were also a few other short comings of the original design that became apparent during firmware development. The choice of shift registers to drive the LED displays instead of multiplexing made writing the display driver code simple but gave up the brightness control (from PoV) that comes "for free" with that approach. The 74HC595 has an output enable control signal that _maybe_ could be used to achieve a similar effect, so this revision includes a remapping of some of the signals, including the user LEDs, to pins on the MCU that support hardware PWM.
22+
23+
As the objective of this project is as much learning as arriving at a finished "product", I took the opportunity to try out a number of new things as well. Did you know that PCBWay does sheet metal bending?
24+
25+
![final product](./hardware/assets/rev1.jpg)
26+
27+
### Pushing the Envelope
28+
With a green light to "showcase capabilities" from PCBWay, I wanted to make something worth showing off. After reviewing the website and discovering their 3D printing/CNC milling service I decided to design something that would take advantage of more than just PCBWay's PCB printing capabilities. While the core of this project would remain a development board for STM32F1 hardware and software design, this sponsored revision would be the "STM32 Shift Clock". After a brief affair with a split board design I landed on the simple board and stand design above. This design allowed me to highlight three of PCBWay's capabilities that make them great board partners for hobbyists and professionals alike. First, the PCB is a 4-layer design utilizing [via-in-pad to simplify the routing](./hardware/assets/via-in-pad.PNG) between the shift registers and LEDs characters. PCBWay's engineers were great to work with on this and made the process of indicating which vias needed to be filled and capped simple. Which was really great because KiCad does not currently have a way to indicate if a specific via should be filled. Second, I moved all of the tiny (i.e. too hard to hand solder) components to the back of the board and relied on PCBWay's turnkey PCB assembly service to source the parts and do the hard work of getting all those tiny bits in the right spots. Again, PCBWay's engineers made the process of approving the parts selection and confirming placement and orientation easy. I used LCSC as the supplier for all of the parts on this board and all I had to do was indicate the LCSC part numbers and PCBWay took care of the rest. Their engineers even sent pictures of the boards in production so that I could visually inspect all of the parts placements. And finally, the stand. I've only ever dabbled in 3D printing and certainly never with metal. So when I saw that PCBWay could do low volume sheet metal bending I had to try it out. What followed was a caffeine fueled afternoon of YouTube tutorials and frustrating attempts to get the idea in my head out into FreeCAD. In the end I went with a very simple design that displays the PCB nicely while keeping it from tipping/sliding when pressing the buttons. I chose 5052 Aluminum with a bead blasted and anodized finish to match the PCB. Given the materials and manual labor involved I was really pleased with how reasonable the quoted cost was per piece. All in all I was very impressed with my interactions with PCBWay for this project. From the initial design submissions to the engineering verification and the customer service interactions PCBWay helped me take an idea from mind to matter and I look forward to working with them again on my future projects.
29+
530
## Schematic
631
![current schematic](./hardware/assets/stm32-shift-display.svg)
732

8-
## Board Design
33+
## Board Render
934
![current board](./hardware/assets/stm32-shift-display-front.png)

firmware/.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"files.associations": {
3+
"ostream": "cpp"
4+
},
5+
"cSpell.words": [
6+
"pinmap"
7+
]
8+
}

firmware/lib/ShiftDisplay/src/ShiftDisplay.cpp

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,36 @@
1111
#include <Arduino.h>
1212
#include "./ShiftDisplay.h"
1313

14-
#define DP_BP 7
15-
#define DP_BM 0b01000000
14+
#define DP_BP 1
15+
#define DP_BM 0b00000001
16+
#define GET_BIT(byte, bit) (((byte) >> (bit)) & 0x01)
1617

1718
/**
1819
* Bit Position: 7 6 5 5 3 2 1 0
1920
* 595 Outputs: QA QB QC QD QE QF QG QH
20-
* 7 Segment LED: B DP C A F D G E
21+
* 7 Segment LED: A B C D E F G DP
2122
*/
2223
static const uint8_t segment_data[] = {
23-
0b10111101, // '0'
24-
0b10100000, // '1'
25-
0b10010111, // '2'
26-
0b10110110, // '3'
27-
0b10101010, // '4'
28-
0b00111110, // '5'
29-
0b00111111, // '6'
30-
0b10110000, // '7'
31-
0b10111111, // '8'
32-
0b10111110, // '9'
33-
0b10111011, // 'A'
34-
0b00101111, // 'B' (b)
35-
0b00011101, // 'C'
36-
0b10100111, // 'D' (d)
37-
0b00011111, // 'E'
38-
0b00011011 // 'F'
24+
0b11111100, // '0'
25+
0b01100000, // '1'
26+
0b11011010, // '2'
27+
0b11110010, // '3'
28+
0b01100110, // '4'
29+
0b10110110, // '5'
30+
0b10111110, // '6'
31+
0b11100000, // '7'
32+
0b11111110, // '8'
33+
0b11110110, // '9'
34+
0b11101110, // 'A'
35+
0b00011110, // 'B' (b)
36+
0b10011101, // 'C'
37+
0b01111010, // 'D' (d)
38+
0b10011110, // 'E'
39+
0b10001110, // 'F'
40+
0b10111100, // 'G'
41+
0b01101110, // 'H'
42+
0b00000000, // ' ' (space)
43+
3944
};
4045

4146
ShiftDisplay::ShiftDisplay(uint16_t data, uint16_t sclk, uint16_t sclr, uint16_t rclk, uint16_t oe)
@@ -50,6 +55,45 @@ ShiftDisplay::ShiftDisplay(uint16_t data, uint16_t sclk, uint16_t sclr, uint16_t
5055
_delay_ms = 0;
5156
}
5257

58+
void ShiftDisplay::update_buffer(const char *new_content)
59+
{
60+
if (new_content == nullptr)
61+
{
62+
return;
63+
}
64+
65+
size_t len = strlen(new_content);
66+
if (len > _char_buffer_size)
67+
{
68+
len = _char_buffer_size;
69+
}
70+
71+
for (size_t i = 0; i < len; i++)
72+
{
73+
_buffer[i] = new_content[i];
74+
}
75+
}
76+
77+
void ShiftDisplay::update_character(uint8_t index, char ascii, bool dp)
78+
{
79+
if (index >= _char_buffer_size)
80+
{
81+
return;
82+
}
83+
84+
_buffer[index] = ascii;
85+
_dp_state = dp ? SET_BIT(_dp_state, index) : CLEAR_BIT(_dp_state, index);
86+
}
87+
88+
void ShiftDisplay::update_display()
89+
{
90+
// Shift out the buffer in reverse order
91+
for (int i = _char_buffer_size - 1; i >= 0; i--)
92+
{
93+
shiftOutAscii(_buffer[i], GET_BIT(_dp_state, i));
94+
}
95+
}
96+
5397
void ShiftDisplay::begin(uint32_t delay_us)
5498
{
5599
pinMode(_serial_data_pin, OUTPUT);
@@ -69,18 +113,29 @@ void ShiftDisplay::begin(uint32_t delay_us)
69113
_initialized = true;
70114
}
71115

116+
void ShiftDisplay::update()
117+
{
118+
if (!_initialized)
119+
{
120+
return;
121+
}
122+
123+
update_display();
124+
}
125+
72126
uint8_t ShiftDisplay::map_ascii(char hex_char)
73127
{
74128
// Error checking: ensure input is a valid hexadecimal character
75-
if (!((hex_char >= '0' && hex_char <= '9') || (hex_char >= 'A' && hex_char <= 'F')))
129+
if (!((hex_char >= '0' && hex_char <= '9') || (hex_char >= 'A' && hex_char <= 'H') || (hex_char == ' ')))
76130
{
77-
if (hex_char == ' ')
78-
{
79-
return 0;
80-
}
81131
return 0b10101000; // Indicate invalid character with 3 horizontal bars
82132
}
83133

134+
if (hex_char == ' ')
135+
{
136+
return 0;
137+
}
138+
84139
// Convert character to index (subtract ASCII offset)
85140
uint8_t index = (hex_char <= '9') ? (hex_char - '0') : (hex_char - 'A' + 10);
86141

@@ -122,6 +177,13 @@ void ShiftDisplay::shiftOutAscii(char ascii, bool dp)
122177
shiftOutByte(map_ascii(ascii), dp);
123178
}
124179

180+
void ShiftDisplay::writeDisplay(const char *buffer, uint8_t dp)
181+
{
182+
update_buffer(buffer);
183+
_dp_state = dp;
184+
update_display();
185+
}
186+
125187
void ShiftDisplay::enable()
126188
{
127189
digitalWrite(_output_en_pin, LOW);

firmware/lib/ShiftDisplay/src/ShiftDisplay.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,16 @@ class ShiftDisplay: public Print
2727
uint32_t _delay_us;
2828
uint32_t _delay_ms;
2929

30+
// Private display state vars
31+
uint8_t _dp_state = 0b00000000; // [0:5] -> [0:5] DP state for each character, [6:7] -> Not used
32+
static const uint8_t _char_buffer_size = 6;
33+
char _buffer[_char_buffer_size] = {' ', ' ', ' ', ' ', ' ', ' '};
34+
3035
protected:
3136
uint8_t map_ascii(char hex_char);
37+
void update_buffer(const char* new_content);
38+
void update_character(uint8_t index, char ascii, bool dp);
39+
void update_display();
3240

3341
public:
3442
ShiftDisplay(uint16_t data, uint16_t sclk, uint16_t sclr, uint16_t rclk, uint16_t oe);
@@ -38,6 +46,8 @@ class ShiftDisplay: public Print
3846
begin(0U);
3947
}
4048

49+
void update();
50+
4151
void shiftOutByte(uint8_t byte, bool dp);
4252
void shiftOutByte(uint8_t byte)
4353
{
@@ -50,6 +60,8 @@ class ShiftDisplay: public Print
5060
shiftOutAscii(ascii, false);
5161
}
5262

63+
void writeDisplay(const char* buffer, uint8_t dp);
64+
5365
void enable();
5466
void disable();
5567

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "ShiftDisplayFSM.h"
2+
3+
void ShiftDisplayFSM::update()
4+
{
5+
currentState = nextState;
6+
}
7+
8+
void ShiftDisplayFSM::setState(State state)
9+
{
10+
nextState = state;
11+
}
12+
13+
State ShiftDisplayFSM::getState()
14+
{
15+
return currentState;
16+
}
17+
18+
void ShiftDisplayFSM::execute(Action action)
19+
{
20+
switch (action)
21+
{
22+
case Action::MENU_ENTER:
23+
if (currentState == State::IDLE)
24+
{
25+
nextState = State::MENU;
26+
nextMenuState = MenuState::MENU_SET_TIME;
27+
}
28+
break;
29+
case Action::MENU_EXIT:
30+
nextState = State::IDLE;
31+
nextMenuState = MenuState::MENU_NONE;
32+
break;
33+
case Action::MENU_UP:
34+
break;
35+
case Action::MENU_DOWN:
36+
break;
37+
case Action::MENU_SELECT:
38+
if (currentState == State::IDLE)
39+
break;
40+
switch (currentMenuState)
41+
{
42+
case MenuState::MENU_SET_TIME:
43+
nextMenuState = MenuState::MENU_SET_HOUR;
44+
break;
45+
case MenuState::MENU_SET_DATE:
46+
nextMenuState = MenuState::MENU_SET_DAY;
47+
48+
break;
49+
default:
50+
break;
51+
}
52+
}
53+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef _SHIFTDISPLAYFSM_H
2+
#define _SHIFTDISPLAYFSM_H
3+
4+
#include <inttypes.h>
5+
6+
enum State {
7+
IDLE, MENU
8+
};
9+
10+
enum MenuState {
11+
MENU_NONE, MENU_SET_TIME, MENU_SET_DATE, MENU_SET_HOUR, MENU_SET_MINUTE, MENU_SET_SECOND, MENU_SET_DAY, MENU_SET_MONTH, MENU_SET_YEAR
12+
};
13+
14+
enum Action {
15+
ACTION_NONE, MENU_ENTER, MENU_EXIT, MENU_UP, MENU_DOWN, MENU_SELECT
16+
};
17+
18+
class ShiftDisplayFSM {
19+
private:
20+
State currentState;
21+
State nextState;
22+
23+
MenuState currentMenuState;
24+
MenuState nextMenuState;
25+
26+
public:
27+
ShiftDisplayFSM() : currentState(IDLE), nextState(IDLE), currentMenuState(MENU_NONE), nextMenuState(MENU_NONE) {}
28+
void update();
29+
void execute(Action action);
30+
void setState(State state);
31+
State getState();
32+
};
33+
34+
35+
36+
#endif // _SHIFTDISPLAYFSM_H

firmware/platformio.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ build_flags =
2525
lib_deps =
2626
stm32duino/STM32duino RTC @ ^1.4.0
2727
stm32duino/STM32duino Low Power @ ^1.2.5
28-
bxparks/AceButton @ ^1.10.1
28+
bxparks/AceButton @ ^1.10.1
29+
ppedro74/SerialCommands @ ^2.2.0

0 commit comments

Comments
 (0)