Merge pull request #2 from Blitblank/feature/ssd-driver

Feature/ssd driver
This commit is contained in:
Preston McGee
2025-12-14 18:58:12 -06:00
committed by GitHub
13 changed files with 315 additions and 19 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\

View File

@@ -9,23 +9,20 @@
#include "pins.hpp" #include "pins.hpp"
#include "DemoTask.hpp"
App::App() { App::App() {
ESP_LOGI(TAG, "App constructor"); ESP_LOGI(TAG, "App constructor");
} }
uint32_t App::main() { uint32_t App::main() {
ESP_LOGI(TAG, "Example configured to blink GPIO LED!"); ESP_LOGI(__FILE__, "Running App::main()");
gpio_reset_pin(gpio_onboardLed);
gpio_set_direction(gpio_onboardLed, GPIO_MODE_OUTPUT);
while (1) { static DemoTask demoTask{};
ESP_LOGI(TAG, "Turning the LED %s!", ledState == true ? "ON" : "OFF");
gpio_set_level(gpio_onboardLed, ledState);
/* Toggle the LED state */
ledState = !ledState;
vTaskDelay(blinkTime / portTICK_PERIOD_MS);
}
return 1; // unreachable ESP_LOGI(__FILE__, "Starting DemoTask");
demoTask.start("DemoTask", 4096, 5, 1);
return 1;
} }

View File

@@ -16,6 +16,6 @@ private:
uint32_t ledState = 0; uint32_t ledState = 0;
uint32_t blinkTime = 250; uint32_t blinkTime = 250;
const char *TAG = "app"; const char *TAG = "app"; // TODO: instead of this for logging you can use __FILE__ or __func__
}; };

View File

@@ -1,5 +1,9 @@
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}

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