a lot of fixes

This commit is contained in:
Marvin 2025-07-22 16:08:55 +02:00
parent 576b481c13
commit 4989f763c7
10 changed files with 214 additions and 225 deletions

View File

@ -11,6 +11,9 @@
"kernel.h": "c",
"lora.h": "c",
"spi.h": "c",
"gpio.h": "c"
"gpio.h": "c",
"array": "c",
"string": "c",
"string_view": "c"
}
}

View File

@ -0,0 +1,51 @@
# SPDX-License-Identifier: Apache-2.0
description: SSD16xx ePaper display controller
compatible: "solomon,ssd1680"
include: [spi-device.yaml, display-controller.yaml]
properties:
dc-gpios:
type: phandle-array
required: true
description: Data/Command control pin
reset-gpios:
type: phandle-array
required: true
description: Reset GPIO
busy-gpios:
type: phandle-array
required: true
description: Busy GPIO
cs-gpios:
type: phandle-array
required: true
description: Chip-Select GPIO
width:
type: int
required: true
description: Width in pixels
height:
type: int
required: true
description: Height in pixels
rotation:
type: int
required: false
enum: [0, 90, 180, 270]
default: 0
description: Display rotation
mipi-max-frequency:
type: int
required: false
description: Max frequency of the MIPI interface in Hz

View File

@ -12,6 +12,7 @@
#define ALLSCREEN_GRAGHBYTES 3904 //(BYTES_PER_LINE * HEIGHT)
void epd_init(void);
void epd_update(void);
void epd_clear_black(void);
void epd_clear_white(void);

View File

@ -5,6 +5,8 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/lora.h>
void lora_init(void);
// Zugriff auf das LoRa-Device
extern const struct device *lora_dev;

11
include/lora/lorapacket.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef LORA_PACKET_H
#define LORA_PACKET_H
#include <stdint.h>
typedef struct {
char message[20];
uint32_t timestamp;
} __attribute__((packed)) DataPacket;
#endif // LORA_PACKET_H

View File

@ -8,5 +8,9 @@ CONFIG_PRINTK=y
CONFIG_LORA=y
CONFIG_LORA_SHELL=y
# Display
CONFIG_DISPLAY=y
CONFIG_SSD16XX=y
# --- Stack Size (unverändert) ---
CONFIG_MAIN_STACK_SIZE=2048

View File

@ -17,6 +17,8 @@
#define EPD_NODE DT_NODELABEL(epd)
#define SPI_BUS DT_BUS(EPD_NODE)
void epd_clear(void);
// GPIOs
static const struct gpio_dt_spec epd_cs = GPIO_DT_SPEC_GET(EPD_NODE, cs_gpios);
static const struct gpio_dt_spec epd_dc = GPIO_DT_SPEC_GET(EPD_NODE, dc_gpios);
@ -26,17 +28,10 @@ static const struct gpio_dt_spec epd_busy = GPIO_DT_SPEC_GET(EPD_NODE, busy_gpi
// SPI
static const struct device *spi_dev = DEVICE_DT_GET(SPI_BUS);
static struct display_buffer_descriptor desc = {
.width = 250,
.height = 122,
.pitch = 32, // WICHTIG!
.buf_size = 3904,
};
static struct spi_config spi_cfg = {
.frequency = 2000000,
.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
.cs = NULL//{.gpio = {0}}, //war NULL
.cs = { .gpio = {0}, .delay = 0 },
};
@ -50,9 +45,6 @@ static struct spi_config spi_cfg = {
#define isEPD_W21_BUSY gpio_pin_get_dt(&epd_busy)
const uint8_t lut_full_update[] = {
//0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, 0x66, 0x69,
//0x69, 0x59, 0x58, 0x99, 0x99, 0x88, 0x00, 0x00, 0x00, 0x00,
//0xF8, 0xB4, 0x13, 0x51, 0x35, 0x51, 0x51, 0x19, 0x01, 0x00
0xA0, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -64,7 +56,7 @@ void driver_delay_xms(int32_t xms) {
k_msleep(xms);
}
void Epaper_Spi_WriteByte(uint8_t value) {
void epd_spi_writeByte(uint8_t value) {
// Lokaler, nicht optimierbarer Puffer mit expliziter Größe
volatile uint8_t tx_data[1]; // Deklariere tx_data lokal, ohne static
tx_data[0] = value; // Initialisiere den Puffer JEDES Mal
@ -85,89 +77,89 @@ void Epaper_Spi_WriteByte(uint8_t value) {
}
void Epaper_READBUSY(void) {
void epd_readbusy(void) {
while (isEPD_W21_BUSY) {
k_msleep(100);
}
}
void Epaper_Write_Command(uint8_t cmd) {
void epd_write_command(uint8_t cmd) {
EPD_W21_DC_0; // DC auf Command setzen
Epaper_Spi_WriteByte(cmd);
epd_spi_writeByte(cmd);
}
void Epaper_Write_Data(uint8_t data) {
void epd_write_data(uint8_t data) {
EPD_W21_DC_1; // DC auf Data setzen
Epaper_Spi_WriteByte(data);
epd_spi_writeByte(data);
}
void EPD_HW_Init(void) {
void edp_hw_init(void) {
EPD_W21_RST_0;
k_msleep(10); // Ein kurzer Reset-Puls ist oft besser
EPD_W21_RST_1;
k_msleep(100);
Epaper_READBUSY();
Epaper_Write_Command(0x12); // Software Reset
Epaper_READBUSY();
epd_readbusy();
epd_write_command(0x12); // Software Reset
epd_readbusy();
// NEUE, ERGÄNZTE BEFEHLE FÜR BILDQUALITÄT
Epaper_Write_Command(0x0C); // Booster Soft Start Control
Epaper_Write_Data(0xD7);
Epaper_Write_Data(0xD6);
Epaper_Write_Data(0x9D);
epd_write_command(0x0C); // Booster Soft Start Control
epd_write_data(0xD7);
epd_write_data(0xD6);
epd_write_data(0x9D);
Epaper_Write_Command(0x2C); // VCOM-Spannung einstellen
Epaper_Write_Data(0xA8); // VCOM-Wert, sehr wichtig für den Kontrast
epd_write_command(0x2C); // VCOM-Spannung einstellen
epd_write_data(0xA8); // VCOM-Wert, sehr wichtig für den Kontrast
Epaper_Write_Command(0x3A); // Setzt die Dauer eines Frames
Epaper_Write_Data(0x1A); // 50Hz
epd_write_command(0x3A); // Setzt die Dauer eines Frames
epd_write_data(0x1A); // 50Hz
Epaper_Write_Command(0x3B); // Setzt die Gate-Breite
Epaper_Write_Data(0x08);
epd_write_command(0x3B); // Setzt die Gate-Breite
epd_write_data(0x08);
// BEKANNTE BEFEHLE
Epaper_Write_Command(0x01); // Driver output control
Epaper_Write_Data(0x79); // Höhe (122-1)
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x00);
epd_write_command(0x01); // Driver output control
epd_write_data(0x79); // Höhe (122-1)
epd_write_data(0x00);
epd_write_data(0x00);
Epaper_Write_Command(0x11); // Data entry mode
Epaper_Write_Data(0x01); // X-increment, Y-increment
epd_write_command(0x11); // Data entry mode
epd_write_data(0x01); // X-increment, Y-increment
Epaper_Write_Command(0x44); // RAM X start/end
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x1F); // 31 (für 32 Bytes Breite)
epd_write_command(0x44); // RAM X start/end
epd_write_data(0x00);
epd_write_data(0x1F); // 31 (für 32 Bytes Breite)
Epaper_Write_Command(0x45); // RAM Y start/end
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x79); // 121
Epaper_Write_Data(0x00);
epd_write_command(0x45); // RAM Y start/end
epd_write_data(0x00);
epd_write_data(0x00);
epd_write_data(0x79); // 121
epd_write_data(0x00);
Epaper_Write_Command(0x3C); // BorderWaveform
Epaper_Write_Data(0x05); // VBD transition
epd_write_command(0x3C); // BorderWaveform
epd_write_data(0x05); // VBD transition
Epaper_Write_Command(0x18); // Temperature Sensor Control
Epaper_Write_Data(0x80); // Internen Sensor verwenden
epd_write_command(0x18); // Temperature Sensor Control
epd_write_data(0x80); // Internen Sensor verwenden
// Schreibe die finale LUT in den Controller
Epaper_Write_Command(0x32);
epd_write_command(0x32);
for (int i = 0; i < sizeof(lut_full_update); i++) {
Epaper_Write_Data(lut_full_update[i]);
epd_write_data(lut_full_update[i]);
}
// Setze die RAM-Adresszähler auf den Anfang
Epaper_Write_Command(0x4E); // RAM X addr count
Epaper_Write_Data(0x00);
Epaper_Write_Command(0x4F); // RAM Y addr count
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x00);
epd_write_command(0x4E); // RAM X addr count
epd_write_data(0x00);
epd_write_command(0x4F); // RAM Y addr count
epd_write_data(0x00);
epd_write_data(0x00);
Epaper_READBUSY();
epd_readbusy();
}
void initialise(void){
void epd_init(void){
// GPIOs und Geräte-Checks
if (!device_is_ready(spi_dev) || !device_is_ready(epd_cs.port)) {
@ -180,97 +172,97 @@ void initialise(void){
gpio_pin_configure_dt(&epd_busy, GPIO_INPUT);
// Initialisierungssequent für den Chip
EPD_HW_Init();
edp_hw_init();
// 2. Display ZWEIMAL explizit reinigen, um Kaltstart-Artefakte zu entfernen
EPD_Clear(); // Erster Clear-Durchgang
EPD_Clear(); // Zweiter Clear-Durchgang
// Display ZWEIMAL explizit reinigen, um Kaltstart-Artefakte zu entfernen
epd_clear(); // Erster Clear-Durchgang
epd_clear(); // Zweiter Clear-Durchgang
// vor dem Malen den Cursor auf den Anfang (0,0) setzen
EPD_W21_CS_0;
Epaper_Write_Command(0x4E); // Setze X-Adress-Zähler auf 0
Epaper_Write_Data(0x00);
Epaper_Write_Command(0x4F); // Setze Y-Adress-Zähler auf 0
Epaper_Write_Data(0x00);
Epaper_Write_Data(0x00);
epd_write_command(0x4E); // Setze X-Adress-Zähler auf 0
epd_write_data(0x00);
epd_write_command(0x4F); // Setze Y-Adress-Zähler auf 0
epd_write_data(0x00);
epd_write_data(0x00);
EPD_W21_CS_1;
}
void EPD_Update(void) {
Epaper_Write_Command(0x22);
Epaper_Write_Data(0xFF);
Epaper_Write_Command(0x20);
Epaper_READBUSY();
void epd_update(void) {
epd_write_command(0x22);
epd_write_data(0xFF);
epd_write_command(0x20);
epd_readbusy();
}
// Beispiel für das Senden eines Bildes
void EPD_WhiteScreen_ALL(const uint8_t *datas) {
void epd_whitescreen_all(const uint8_t *datas) {
EPD_W21_CS_0; // <-- CS hier aktivieren (LOW)
Epaper_Write_Command(0x24); // Command: write RAM for black/white image
epd_write_command(0x24); // Command: write RAM for black/white image
for (int i = 0; i < ALLSCREEN_GRAGHBYTES; i++) {
Epaper_Write_Data(datas[i]);
epd_write_data(datas[i]);
}
EPD_W21_CS_1; // <-- CS hier deaktivieren (HIGH)
// Der Update-Befehl ist eine eigene Transaktion
EPD_W21_CS_0;
EPD_Update();
epd_update();
EPD_W21_CS_1;
}
// Beispiel für das komplette Löschen des Bildschirms
void EPD_WhiteScreen_Black(void) {
void epd_whitescreen_black(void) {
EPD_W21_CS_0; // <-- CS hier aktivieren (LOW)
Epaper_Write_Command(0x24);
epd_write_command(0x24);
for (int i = 0; i < ALLSCREEN_GRAGHBYTES; i++) {
Epaper_Write_Data(0x00); // Schwarz
epd_write_data(0x00); // Schwarz
}
EPD_W21_CS_1; // <-- CS hier deaktivieren (HIGH)
// Der Update-Befehl ist eine eigene Transaktion
EPD_W21_CS_0;
EPD_Update();
epd_update();
EPD_W21_CS_1;
}
// Dasselbe musst du für EPD_WhiteScreen_White() machen.
void EPD_WhiteScreen_White(void) {
void epd_whitescreen_white(void) {
EPD_W21_CS_0; // <-- CS hier aktivieren (LOW)
Epaper_Write_Command(0x24);
epd_write_command(0x24);
for (int i = 0; i < ALLSCREEN_GRAGHBYTES; i++) {
Epaper_Write_Data(0xFF); // Weiß
epd_write_data(0xFF); // Weiß
}
EPD_W21_CS_1; // <-- CS hier deaktivieren (HIGH)
EPD_W21_CS_1; // <-- CS hier deaktivieren (HIGH)
// Der Update-Befehl ist eine eigene Transaktion
EPD_W21_CS_0;
EPD_Update();
epd_update();
EPD_W21_CS_1;
}
void EPD_Clear(void) {
void epd_clear(void) {
printk("🧹 Clearing display...\n");
// RAM für das Bild mit "Weiß" füllen
EPD_W21_CS_0;
Epaper_Write_Command(0x24); // Schreibe in den RAM
epd_write_command(0x24); // Schreibe in den RAM
for (int i = 0; i < ALLSCREEN_GRAGHBYTES; i++) {
Epaper_Write_Data(0xFF); // 0xFF = Weiß
epd_write_data(0xFF); // 0xFF = Weiß
}
EPD_W21_CS_1;
// Physisches Update starten, um den Bildschirm zu reinigen
EPD_W21_CS_0;
EPD_Update();
epd_update();
EPD_W21_CS_1;
Epaper_READBUSY(); // Warten, bis der Clear-Vorgang abgeschlossen ist
epd_readbusy(); // Warten, bis der Clear-Vorgang abgeschlossen ist
}
@ -281,16 +273,16 @@ void draw_something(char toDraw) {
// Muster in den Display-RAM schreiben
printk("Schreibe Bild in den Display-RAM...\n");
EPD_W21_CS_0;
Epaper_Write_Command(0x24);
epd_write_command(0x24);
for (int i = 0; i < ALLSCREEN_GRAGHBYTES; i++) {
Epaper_Write_Data(HALLO[i]);
epd_write_data(HALLO[i]);
}
EPD_W21_CS_1;
// Physisches Update
printk("Aktualisiere Display...\n");
EPD_W21_CS_0;
EPD_Update();
epd_update();
EPD_W21_CS_1;
printk("✅ Display-Finish! Das Ergebnis sollte jetzt immer perfekt sein.\n");

View File

@ -3,12 +3,27 @@
#include <zephyr/drivers/lora.h>
#include <string.h>
#include "epaper/epaper.h"
#include "lora/synch.h"
#include "lora/lora.h"
#include "lora/lorapacket.h"
#define SLEEP_INTERVAL_MS 120000
#define STACK_SIZE 1024
#define PRIORITY 5
const struct device *lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
static volatile int rx_count = 0;
static volatile bool message_received = false;
K_THREAD_STACK_DEFINE(lora_stack_area, STACK_SIZE);
struct k_thread lora_thread_data;
uint32_t samples[MAX_SAMPLES];
static struct lora_modem_config config = {
.frequency = 868100000,
.bandwidth = BW_125_KHZ,
@ -19,15 +34,16 @@ static struct lora_modem_config config = {
.tx = false,
};
static char msg_buffer[8] = {0};
static DataPacket packet;
void lora_receive_cb(const struct device *dev, uint8_t *data, uint16_t size,
int16_t rssi, int8_t snr, void *user_data)
{
if (size > 0 && size < sizeof(msg_buffer)) {
memcpy(msg_buffer, data, size);
msg_buffer[size] = '\0';
printk("📡 Nachricht empfangen (%d Bytes): %s\n", size, msg_buffer);
if (size == sizeof(DataPacket)) {
memcpy(&packet, data, sizeof(DataPacket));
rx_count++;
message_received = true;
printk("📡 [%d]Nachricht empfangen (%d Bytes): %s\n", rx_count, size, packet.message);
}
}
@ -35,6 +51,7 @@ int enable_lora_receive(void)
{
// Empfang sicher neu starten
lora_recv_async(lora_dev, NULL, NULL);
memset(&samples, 0, sizeof(samples));
int ret = lora_config(lora_dev, &config);
if (ret != 0) {
@ -64,47 +81,29 @@ int disable_lora_receive(void)
return 0;
}
void lora_check_fn(struct k_work *work)
void lora_thread(void *p1, void *p2, void *p3)
{
printk("🔔 Starte LoRa-Empfang\n");
clear_msg_buffer();
while (1) {
message_received = false;
enable_lora_receive();
k_msleep(10); // Stabilisierung
k_msleep(ACTIVE_WINDOW_MS);
enable_lora_receive();
if (msg_buffer != ""){
printk("Nachricht gelesen!");
disable_lora_receive();
draw_something('4');
}
k_work_schedule(&lora_check_work, K_MSEC(SLEEP_INTERVAL_MS));
/* if (enable_lora_receive() == 0) {
for (int i = 0; i < ACTIVE_WINDOW_MS / 100; i++) {
k_msleep(100);
if (message_received) {
draw_something('4');
}
disable_lora_receive();
k_msleep(SLEEP_INTERVAL_MS);
}
const char *msg = get_last_msg();
if (msg[0] != '\0') {
printk("📨 Neue Nachricht: %s\n", msg);
draw_something(msg[0]);
} else {
printk("📭 Keine Nachricht empfangen\n");
}
k_work_schedule(&lora_check_work, K_MSEC(SLEEP_INTERVAL_MS));*/
}
const char *get_last_msg(void)
void lora_init(void)
{
return msg_buffer;
}
void clear_msg_buffer(void)
{
memset(msg_buffer, 0, sizeof(msg_buffer));
}
lora_recv_async(lora_dev, NULL, NULL); // sicher deaktivieren
k_thread_create(&lora_thread_data, lora_stack_area, STACK_SIZE,
lora_thread, NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
}

View File

@ -8,101 +8,37 @@
#include "utils/buttons.h"
#include "utils/leds.h"
#include "lora/lora.h"
#include "lora/synch.h"
K_WORK_DEFINE(sync_work, synchronite);
// Sync-Variablen
uint32_t offsets[MAX_SAMPLES];
uint8_t offset_count = 0;
uint32_t sync_offset = 0;
uint32_t sync_offset_s = 0;
uint32_t samples[MAX_SAMPLES];
BUILD_ASSERT(DT_NODE_HAS_STATUS_OKAY(DEFAULT_RADIO_NODE),
"No default LoRa radio specified in DT");
// 📡 Callback
void lora_receive_cb(const struct device *dev, uint8_t *data, uint16_t size,
int16_t rssi, int8_t snr, void *user_data)
uint32_t calculate_offset(uint32_t samples[MAX_SAMPLES])
{
if ((size >= 4) && (!synchronized)) {
uint32_t master_timer_value = sys_get_le32(data); //uint8_t master_timer_value = data[0] % 60;
uint32_t local_now = k_uptime_get();
uint32_t offsets = 0;
printk("📡 Empfangen: %u ms\n", master_timer_value);
//gpio_pin_toggle_dt(&led_green);
if (offset_count < MAX_SAMPLES) {
samples[offset_count] = local_now + master_timer_value;
offset_count++;
}
if (offset_count >= MAX_SAMPLES) {
uint32_t offsets = 0;
printk("Intervall: %d: %d: %d\n",(MAX_SAMPLES/5), (MAX_SAMPLES*4/5), (MAX_SAMPLES * 3/5));
for (int i = (MAX_SAMPLES/5); i < (MAX_SAMPLES*4/5); i++) {
offsets += samples[i];
}
sync_offset_s = offsets /( MAX_SAMPLES * 3/5);
sync_offset_s -= local_now;
synchronized = true;
int ret = lora_recv_async(lora_dev, NULL, NULL);
if (ret != 0){
printk("Problem beim deaktivieren der Synchronsation: %d\n", ret);
}
printk("✅ Synchronisiert! Offset: %u Sekunden\n", sync_offset_s);
return;
}
printk("Intervall: %d: %d: %d\n",(MAX_SAMPLES/5), (MAX_SAMPLES*4/5), (MAX_SAMPLES * 3/5));
for (int i = (MAX_SAMPLES/5); i < (MAX_SAMPLES*4/5); i++) {
offsets += samples[i];
}
sync_offset_s = offsets /( MAX_SAMPLES * 3/5);
sync_offset_s -= k_uptime_get();
synchronized = true;
printk("✅ Synchronisiert! Offset: %u Sekunden\n", sync_offset_s);
return;
}
// 🕒 Hilfsfunktion: synchronisierter Timerwert
uint32_t get_synced_timer() {
if (!synchronized) return 0;
uint32_t now = k_uptime_get();
return (sync_offset > now) ? (sync_offset - now) : 0;
}
// 🔘 Button Interrupt
void button_pressed(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
printk("🔘 Button gedrückt\n");
k_work_submit(&sync_work); // startet synchronite()
}
void synchronite(struct k_work *work){
uint32_t synchronite(uint32_t samples[MAX_SAMPLES]){
offset_count = 0;
synchronized = false;
memset(offsets, 0, sizeof(offsets));
printk("Initilisierung abgeschlossen");
int ret = lora_config(lora_dev, &config);
ret = lora_recv_async(lora_dev, lora_receive_cb, NULL);
printk("Empfang gestartet (ret = %d)\n", ret);
return calculate_offset(samples);
}
// 🚀 Main
int init(void) {
printk("🔋 Starte LoRa-Empfänger\n");
if (!device_is_ready(lora_dev)) {
printk("❌ LoRa nicht bereit\n");
return -1;
}
while (1) {
k_msleep(1000);
}
}

View File

@ -42,20 +42,10 @@ int main(void)
return -1;
}
/*while(1){
if(nextSynchro > 0){
uint32_t sleepTimer = k_uptime_get() - nextSynchro;
k_msleep(sleepTimer);
}
}*/
initialise();
epd_init();
lora_recv_async(lora_dev, NULL, NULL); // sicher deaktivieren
k_work_init_delayable(&lora_check_work, lora_check_fn);
k_work_schedule(&lora_check_work, K_NO_WAIT);
lora_init();
// Event Loop (muss da sein für Timer und Arbeiten im Hintergrund)
while (1) {
k_sleep(K_SECONDS(1)); // Energiesparend schlafen
}