11 Commits

Author SHA1 Message Date
Preston McGee
1248a0ac39 Merge pull request #2 from Blitblank/feature/ssd-driver
Feature/ssd driver
2025-12-14 18:58:12 -06:00
5cde1bdfce make ssd driver work 2025-12-14 18:51:52 -06:00
a80e918d63 task base 2025-12-14 00:23:30 -06:00
c4644105a7 add drivers and interface and task classes 2025-12-14 00:23:15 -06:00
54c9672ba0 checkpoint 2025-12-11 20:10:35 -06:00
8e9d459345 add driver 2025-12-07 18:20:08 -06:00
68842bbe0f update readme 2025-12-07 16:05:52 -06:00
4588825986 add app 2025-12-07 15:59:56 -06:00
Preston McGee
32ddc11d6e Merge pull request #1 from Blitblank/hello-world
Hello world
2025-12-07 15:06:07 -06:00
Preston McGee
97d7a02218 Merge branch 'main' into hello-world 2025-12-07 15:05:56 -06:00
96ef55e589 added bill of materials 2025-11-30 20:38:10 -06:00
21 changed files with 421 additions and 27 deletions

View File

@@ -10,8 +10,8 @@ Scripts are in scripts directory, named appropriately.
## Development plan: ## Development plan:
- [x] Create repository. - [x] Create repository.
- [ ] Build setup, get working hello-world program. - [x] Build setup, get working hello-world program.
- [ ] Develop 7-segment display driver for the soburg-v2 board, which uses 32-bit shift registers. - [x] Develop 7-segment display driver for the soburg-v2 board, which uses 32-bit shift registers.
Ensures functional hardware interaction and interface design. Ensures functional hardware interaction and interface design.
- [ ] Develop WS2812b driver, would be nice to have some kind of testing for this. - [ ] Develop WS2812b driver, would be nice to have some kind of testing for this.
- [ ] Develop WS2812b interface. I like the FastLed library for Arduino so might have a lot of similar functionality. - [ ] Develop WS2812b interface. I like the FastLed library for Arduino so might have a lot of similar functionality.
@@ -31,7 +31,7 @@ Scripts are in scripts directory, named appropriately.
This is basic but hopefully can be good platform for future WS2812b projects. This is basic but hopefully can be good platform for future WS2812b projects.
## Scripts: ## Scripts:
After cloning: $ ./scripts/repo-setup # installs esp-idf, sets up target configurations After cloning: $ ./scripts/repo-setup # installs esp-idf, sets up target configurations\
To build: $ ./scripts/build.sh To build: $ ./scripts/build.sh\
To flash: $ ./scripts/flash.sh # note: flash.sh automatically builds To flash: $ ./scripts/flash.sh # note: flash.sh automatically builds\
To monitor: $ ./scripts/monitor.sh To monitor: $ ./scripts/monitor.sh\

BIN
pcb/control/TEMPLATE.xlsx Normal file

Binary file not shown.

BIN
pcb/panel/bom.xlsx Normal file

Binary file not shown.

View File

@@ -22084,7 +22084,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "B1911B" (property "Value" "B1911R"
(at 0 -1.43 90) (at 0 -1.43 90)
(layer "B.Fab") (layer "B.Fab")
(uuid "c1484363-b3dc-48b7-9c64-d06df10d96a2") (uuid "c1484363-b3dc-48b7-9c64-d06df10d96a2")
@@ -22764,7 +22764,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "TBDuF" (property "Value" "100uF"
(at 2.5 -5.25 0) (at 2.5 -5.25 0)
(layer "B.Fab") (layer "B.Fab")
(uuid "a592ba06-f886-4233-b793-557c4b352735") (uuid "a592ba06-f886-4233-b793-557c4b352735")
@@ -26393,7 +26393,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "TBDuF" (property "Value" "100uF"
(at 2.5 -5.25 0) (at 2.5 -5.25 0)
(layer "B.Fab") (layer "B.Fab")
(uuid "036ec2e2-300a-42a7-987d-e4811bf430e0") (uuid "036ec2e2-300a-42a7-987d-e4811bf430e0")
@@ -28139,7 +28139,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "TBD" (property "Value" "0.1μF"
(at 0 -1.43 90) (at 0 -1.43 90)
(layer "B.Fab") (layer "B.Fab")
(uuid "2125effb-a0e4-4175-9ce6-cce3daef6fd0") (uuid "2125effb-a0e4-4175-9ce6-cce3daef6fd0")
@@ -29018,7 +29018,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "52.3Ω" (property "Value" "52.3kΩ"
(at 0 -1.43 90) (at 0 -1.43 90)
(layer "B.Fab") (layer "B.Fab")
(uuid "f8ed0ff3-21fe-4dac-b950-096d117b1132") (uuid "f8ed0ff3-21fe-4dac-b950-096d117b1132")
@@ -30643,7 +30643,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "TBD" (property "Value" "100pF"
(at 0 -1.43 90) (at 0 -1.43 90)
(layer "B.Fab") (layer "B.Fab")
(uuid "f93816e5-03a1-48e1-b93c-11d13f82d8e9") (uuid "f93816e5-03a1-48e1-b93c-11d13f82d8e9")
@@ -31613,7 +31613,7 @@
(justify mirror) (justify mirror)
) )
) )
(property "Value" "TBD" (property "Value" "10μF"
(at 0 -1.43 0) (at 0 -1.43 0)
(layer "B.Fab") (layer "B.Fab")
(uuid "4ecb6ecd-64d9-4e42-b908-ec3e49aafb1b") (uuid "4ecb6ecd-64d9-4e42-b908-ec3e49aafb1b")
@@ -45894,7 +45894,6 @@
(polygon (polygon
(pts (pts
(xy 152 87.5) (xy 153.75 85.5) (xy 154.75 84.5) (xy 149 84.5) (xy 150.75 86.25) (xy 150.75 87.5) (xy 152 87.5) (xy 153.75 85.5) (xy 154.75 84.5) (xy 149 84.5) (xy 150.75 86.25) (xy 150.75 87.5)
(xy 150.75 87.5)
) )
) )
(filled_polygon (filled_polygon

View File

@@ -527,11 +527,23 @@
"label": "DNP", "label": "DNP",
"name": "${DNP}", "name": "${DNP}",
"show": true "show": true
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
} }
], ],
"filter_string": "", "filter_string": "",
"group_symbols": true, "group_symbols": true,
"name": "Grouped By Value", "name": "",
"sort_asc": true, "sort_asc": true,
"sort_field": "Reference" "sort_field": "Reference"
}, },

View File

@@ -6143,7 +6143,7 @@
(justify left) (justify left)
) )
) )
(property "Value" "TBD" (property "Value" "0.1μF"
(at 330.2 91.948 0) (at 330.2 91.948 0)
(effects (effects
(font (font
@@ -7559,7 +7559,7 @@
(justify right) (justify right)
) )
) )
(property "Value" "B1911B" (property "Value" "B1911R"
(at 360.68 56.1974 90) (at 360.68 56.1974 90)
(effects (effects
(font (font
@@ -7694,7 +7694,7 @@
(justify left) (justify left)
) )
) )
(property "Value" "TBDuF" (property "Value" "100uF"
(at 365.252 91.948 0) (at 365.252 91.948 0)
(effects (effects
(font (font
@@ -8055,7 +8055,7 @@
(justify left) (justify left)
) )
) )
(property "Value" "TBDuF" (property "Value" "100uF"
(at 357.632 91.948 0) (at 357.632 91.948 0)
(effects (effects
(font (font
@@ -8556,8 +8556,8 @@
(justify left) (justify left)
) )
) )
(property "Value" "TBD" (property "Value" "10μF"
(at 295.91 94.488 0) (at 295.402 94.488 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
@@ -9264,8 +9264,8 @@
(justify left) (justify left)
) )
) )
(property "Value" "TBD" (property "Value" "100pF"
(at 342.9 91.948 0) (at 341.63 92.456 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
@@ -10421,7 +10421,7 @@
) )
) )
) )
(property "Value" "52.3Ω" (property "Value" "52.3kΩ"
(at 348.488 88.646 90) (at 348.488 88.646 90)
(effects (effects
(font (font

View File

@@ -10,6 +10,7 @@ export SDKCONFIG_DEFAULTS=${PWD}/config/sdkconfig.defaults
. ${IDF_PATH}/export.sh . ${IDF_PATH}/export.sh
idf.py set-target esp32s3
idf.py build idf.py build
# idk how to put it in the right place # idk how to put it in the right place

28
src/App.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "App.hpp"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "pins.hpp"
#include "DemoTask.hpp"
App::App() {
ESP_LOGI(TAG, "App constructor");
}
uint32_t App::main() {
ESP_LOGI(__FILE__, "Running App::main()");
static DemoTask demoTask{};
ESP_LOGI(__FILE__, "Starting DemoTask");
demoTask.start("DemoTask", 4096, 5, 1);
return 1;
}

21
src/App.hpp Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include<stdint.h>
class App {
public:
App();
~App() = default;
uint32_t main();
private:
uint32_t ledState = 0;
uint32_t blinkTime = 250;
const char *TAG = "app"; // TODO: instead of this for logging you can use __FILE__ or __func__
};

View File

@@ -1,9 +1,13 @@
file(GLOB SRC_FILES "*.c" "*.cpp") file(GLOB SRC_FILES
"*.c"
"*.cpp"
"drivers/*.c"
)
idf_component_register( idf_component_register(
SRCS ${SRC_FILES} SRCS ${SRC_FILES}
PRIV_REQUIRES spi_flash PRIV_REQUIRES spi_flash
REQUIRES esp_driver_gpio REQUIRES esp_driver_gpio
INCLUDE_DIRS "." INCLUDE_DIRS "."
) )

34
src/DemoTask.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "DemoTask.hpp"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "SsdInterface.hpp"
#include "pins.hpp"
DemoTask::DemoTask() {
}
void DemoTask::run() {
ESP_LOGI(__FILE__, "Demo Task: run()");
ssd_595_t dev = { gpio_ssd_data, gpio_ssd_clk, gpio_ssd_latch, true };
SsdInterface ssd(&dev, ssdDigits);
uint32_t delay = 500; // ms
uint8_t digit = 0;
while(1) {
ssd.writeRaw(&digitMap[digit], 1);
digit++;
if(digit >= 16) digit = 0;
vTaskDelay(delay / portTICK_PERIOD_MS);
}
}

16
src/DemoTask.hpp Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "TaskBase.hpp"
class DemoTask : public TaskBase {
public:
DemoTask();
protected:
void run() override;
private:
const size_t ssdDigits = 4;
};

28
src/SsdInterface.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "SsdInterface.hpp"
SsdInterface::SsdInterface(const ssd_595_t* device, size_t numDigits) : device_(device), numDigits_(numDigits) {
shiftInit(device_);
}
STATUS SsdInterface::writeRaw(uint8_t* bytes, size_t numBytes) {
shiftBytes(device_, bytes, numBytes);
return OKAY;
}
STATUS SsdInterface::write10(int32_t value) {
// TODO: implement
return NOT_IMPLEMENTED;
}
STATUS SsdInterface::write16(int32_t value) {
// TODO: implement
return NOT_IMPLEMENTED;
}
STATUS SsdInterface::get(uint8_t* bytes) {
// TODO: implement
return NOT_IMPLEMENTED;
}

43
src/SsdInterface.hpp Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include "stdint.h"
#include "drivers/ssd.h"
#include "common.h"
class SsdInterface {
public:
SsdInterface(const ssd_595_t* device, size_t numDigits);
~SsdInterface() = default;
// Outputs the data straight to hardware, mostly for testing purposes
// bytes: the data to write, with bits targetting an led on the ssd
// Returns: execution status
STATUS writeRaw(uint8_t* bytes, size_t numBytes);
// Displays a decimal integer on the ssd
// value: the integer to display
// Returns: execution status
STATUS write10(int32_t value);
// Displays a hexadecimal integer on the ssd
// value: the integer to display
// Returns: execution status
STATUS write16(int32_t value);
// Copies the data currently displayed on the ssd to bytes
// bytes: place to write to
// Returns: execution status
STATUS get(uint8_t* bytes);
private:
const ssd_595_t* device_;
size_t numDigits_; // number of chained digits
uint8_t* data_; // pointer to the data written
};

17
src/TaskBase.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include "TaskBase.hpp"
#include "esp_log.h"
void TaskBase::start(const char* name, uint32_t stackSize, UBaseType_t priority, BaseType_t core) {
xTaskCreatePinnedToCore(&TaskBase::taskEntryPoint, name, stackSize, this, priority, &handle, core);
return;
}
void TaskBase::taskEntryPoint(void* param) {
auto* task = static_cast<TaskBase*>(param);
task->run();
vTaskDelete(nullptr);
return;
}

20
src/TaskBase.hpp Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
class TaskBase {
public:
virtual ~TaskBase() = default;
void start(const char* name, uint32_t stackSize, UBaseType_t priority, BaseType_t core = tskNO_AFFINITY);
protected:
virtual void run() = 0;
private:
TaskHandle_t handle = nullptr;
static void taskEntryPoint(void* param);
};

8
src/common.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
enum STATUS {
OKAY = 0,
ERROR = -1,
NOT_IMPLEMENTED = -2,
};

59
src/drivers/ssd.c Normal file
View File

@@ -0,0 +1,59 @@
#include "ssd.h"
#include "esp_rom_sys.h"
#include "esp_log.h"
#ifdef __cplusplus
extern "C" {
#endif
inline void pulse(gpio_num_t pin) {
gpio_set_level(pin, 1);
gpio_set_level(pin, 0);
}
void shiftInit(const ssd_595_t* device) {
gpio_reset_pin(device->dataPin);
gpio_reset_pin(device->clockPin);
gpio_reset_pin(device->latchPin);
gpio_config_t ioConfig = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << device->dataPin) |
(1ULL << device->clockPin) |
(1ULL << device->latchPin),
};
gpio_config(&ioConfig);
gpio_set_level(device->dataPin, 0);
gpio_set_level(device->clockPin, 0);
gpio_set_level(device->latchPin, 0);
ESP_LOGI(__FILE__, "ssd_595 initialized");
}
void addDecimal(uint8_t* data) {
// TODO: fix
// data = (*data | 0x01);
}
void shiftByte(const ssd_595_t* device, uint8_t byte) {
for(int i = 0; i < __CHAR_BIT__ ; i++) {
uint32_t level = ((byte >> i) & 0x1) ^ device->commonCathode;
gpio_set_level(device->dataPin, level);
pulse(device->clockPin);
}
pulse(device->latchPin);
}
void shiftBytes(const ssd_595_t* device, uint8_t* bytes, size_t numBytes) {
for(size_t i = 0; i < numBytes; i++) {
shiftByte(device, bytes[i]);
}
}
#ifdef __cplusplus
}
#endif

70
src/drivers/ssd.h Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include <stdint.h>
#include "driver/gpio.h"
#define SSD_DIGIT_MAP_LENGTH 32
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
gpio_num_t dataPin;
gpio_num_t clockPin;
gpio_num_t latchPin;
bool commonCathode; // false = common anode
} ssd_595_t;
// encoding of digits on the seven segment display
// 0bxxxxxxxx
// ABCDEFG.
static uint8_t digitMap[SSD_DIGIT_MAP_LENGTH] = {
0xFC, // 0
0x60, // 1
0xDA, // 2
0xF2, // 3
0x66, // 4
0xB6, // 5
0xBE, // 6
0xE0, // 7
0xFE, // 8
0xF6, // 9
0xEE, // A
0x3E, // B
0x9C, // C
0x7A, // D
0x9E, // E
0x8E, // F
0x02, // -
0x01, // .
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
0x92, // Error code (not implemented)
};
// TODO: have these return error codes likewise
void shiftInit(const ssd_595_t* device);
void pulse(gpio_num_t pin);
void addDecimal(uint8_t* data); // adds a decimal to a single digit
void shiftByte(const ssd_595_t* device, uint8_t byte); // outputs a serial byte, big-endian
void shiftBytes(const ssd_595_t* device, uint8_t* bytes, size_t numBytes); // outputs multiple bytes
#ifdef __cplusplus
}
#endif

View File

@@ -14,7 +14,7 @@ void app_main(void) {
ESP_LOGI(TAG, "Program start"); ESP_LOGI(TAG, "Program start");
App app; App app;
int8_t status = app.main(); int32_t status = app.main();
ESP_LOGI(TAG, "App main returned status %d", status); ESP_LOGI(TAG, "App main returned status %d", status);
} }

34
src/pins.hpp Normal file
View File

@@ -0,0 +1,34 @@
#include "driver/gpio.h"
// onboard led
const gpio_num_t gpio_onboardLed = GPIO_NUM_2;
// misc
const gpio_num_t gpio_msdDetect = GPIO_NUM_8;
const gpio_num_t gpio_ws2812b = GPIO_NUM_9;
// i2c
const gpio_num_t gpio_i2c_dout = GPIO_NUM_11;
const gpio_num_t gpio_i2c_din = GPIO_NUM_12;
// i2c expander interrupts
const gpio_num_t gpio_i2c_intrA = GPIO_NUM_14;
const gpio_num_t gpio_i2c_intrB = GPIO_NUM_13;
// uart
const gpio_num_t gpio_uart_rx = (gpio_num_t)44; // should already be configured
const gpio_num_t gpio_uart_tx = (gpio_num_t)43; // should already be configured
// seven segment display
const gpio_num_t gpio_ssd_latch = GPIO_NUM_17;
const gpio_num_t gpio_ssd_clk = GPIO_NUM_18;
const gpio_num_t gpio_ssd_data = GPIO_NUM_21;
// i2s
const gpio_num_t gpio_i2s_wsel = GPIO_NUM_38;
const gpio_num_t gpio_i2s_din = GPIO_NUM_39;
const gpio_num_t gpio_i2s_bck = (gpio_num_t)40; // idk why it only goes up to 40
const gpio_num_t gpio_i2s_mck = (gpio_num_t)41;
const gpio_num_t gpio_i2s_mute = (gpio_num_t)42;
const gpio_num_t gpio_i2s_shdn = (gpio_num_t)45;