Initial commit
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
set(dali_srcs
|
||||
"src/dali.c"
|
||||
"src/dali_hal_idf5.c"
|
||||
)
|
||||
|
||||
set(dali_include_dirs "src/include")
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${dali_srcs}
|
||||
INCLUDE_DIRS ${dali_include_dirs}
|
||||
REQUIRES driver esp_timer
|
||||
)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
menu "DALI Component"
|
||||
|
||||
config DALI_PHY_COUNT
|
||||
int "Maximum DALI buses"
|
||||
range 1 16
|
||||
default 16
|
||||
help
|
||||
Maximum number of DALI PHY buses managed by this component.
|
||||
|
||||
config DALI_DEFAULT_BAUDRATE
|
||||
int "Default DALI baudrate"
|
||||
range 400 2400
|
||||
default 1200
|
||||
help
|
||||
Default baudrate used during initialization.
|
||||
|
||||
config DALI_API_QUEUE_LEN
|
||||
int "Global API queue length"
|
||||
range 1 64
|
||||
default 10
|
||||
|
||||
config DALI_TX_QUEUE_LEN
|
||||
int "Per-bus TX queue length"
|
||||
range 1 16
|
||||
default 1
|
||||
|
||||
config DALI_TX_REPLY_QUEUE_LEN
|
||||
int "Per-bus TX reply queue length"
|
||||
range 1 32
|
||||
default 4
|
||||
|
||||
config DALI_RX_QUEUE_LEN
|
||||
int "Per-bus RX queue length"
|
||||
range 1 128
|
||||
default 50
|
||||
|
||||
config DALI_DEBUG_QUEUE_LEN
|
||||
int "Debug queue length"
|
||||
range 1 256
|
||||
default 100
|
||||
|
||||
config DALI_ENABLE_DEBUG_TASK
|
||||
bool "Enable debug task"
|
||||
default n
|
||||
help
|
||||
When enabled, starts a low-priority task that prints RX timing traces.
|
||||
|
||||
config DALI_DALI_TASK_STACK_SIZE
|
||||
int "DALI task stack size"
|
||||
range 1024 8192
|
||||
default 2048
|
||||
|
||||
config DALI_DALI_TASK_PRIORITY
|
||||
int "DALI task priority"
|
||||
range 1 24
|
||||
default 2
|
||||
|
||||
config DALI_DEBUG_TASK_STACK_SIZE
|
||||
int "Debug task stack size"
|
||||
range 1024 8192
|
||||
default 2048
|
||||
|
||||
config DALI_DEBUG_TASK_PRIORITY
|
||||
int "Debug task priority"
|
||||
range 1 24
|
||||
default 1
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,18 @@
|
||||
# DALI Component Migration Notes
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Breaking API cleanup
|
||||
|
||||
- Renamed global queue symbol:
|
||||
- `dali_send_replay_queue` -> `dali_send_reply_queue`
|
||||
|
||||
### Build/config changes
|
||||
|
||||
- Added `Kconfig` options for queue sizes and task tuning.
|
||||
- `components/dali/CMakeLists.txt` now uses a fixed explicit source list for the current ESP-IDF HAL path.
|
||||
|
||||
### Required action
|
||||
|
||||
- Replace all external uses of `dali_send_replay_queue` with `dali_send_reply_queue`.
|
||||
- Run `idf.py reconfigure` after updating to load new Kconfig defaults.
|
||||
@@ -0,0 +1,60 @@
|
||||
# DALI ESP-IDF Component
|
||||
|
||||
This component provides a DALI (IEC 62386) protocol stack and ESP-IDF HAL implementation that can be used directly in native FreeRTOS/ESP-IDF projects.
|
||||
|
||||
## Features
|
||||
|
||||
- DALI forward and backward frame support.
|
||||
- Multi-bus support (`DALI_PHY_COUNT`, default 16).
|
||||
- Runtime baudrate control (`400..2400`, default `1200`).
|
||||
- ESP-IDF GPTimer + GPIO interrupt driven HAL.
|
||||
|
||||
## Public Headers
|
||||
|
||||
- `dali.h`: high-level DALI helpers.
|
||||
- `dali_hal.h`: HAL API and queue access.
|
||||
- `dali_define.h`: DALI command definitions.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Add this component to your project.
|
||||
2. Configure TX/RX pins and initialize at least one bus.
|
||||
3. Send a DALI frame.
|
||||
|
||||
```c
|
||||
#include "dali.h"
|
||||
#include "dali_define.h"
|
||||
#include "dali_hal.h"
|
||||
|
||||
void app_main(void) {
|
||||
ESP_ERROR_CHECK(dali_hal_set_baudrate(1200));
|
||||
ESP_ERROR_CHECK(dali_hal_init(0, 4, 5));
|
||||
|
||||
Dali_msg_t msg = dali_msg_new(0xFF, DALI_CMD_ON); // broadcast ON command
|
||||
msg.id = 0;
|
||||
dali_send(&msg);
|
||||
}
|
||||
```
|
||||
|
||||
## Kconfig Options
|
||||
|
||||
Use `menuconfig` under `DALI Component` to configure:
|
||||
|
||||
- Bus count and default baudrate.
|
||||
- Queue sizes.
|
||||
- Task stack sizes and priorities.
|
||||
- Optional debug task.
|
||||
|
||||
## API Note
|
||||
|
||||
The global TX response queue symbol was renamed:
|
||||
|
||||
- old: `dali_send_replay_queue`
|
||||
- new: `dali_send_reply_queue`
|
||||
|
||||
See `MIGRATION.md` for compatibility notes.
|
||||
|
||||
## Examples
|
||||
|
||||
- `examples/dali_basic`
|
||||
- `examples/dali_multi_bus`
|
||||
@@ -0,0 +1,11 @@
|
||||
version: "0.1.0"
|
||||
description: "DALI (IEC 62386) protocol and HAL component for ESP-IDF"
|
||||
url: "https://github.com/openLuat/luatos-soc-idf5"
|
||||
repository: "https://github.com/openLuat/luatos-soc-idf5.git"
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
tags:
|
||||
- dali
|
||||
- lighting
|
||||
- esp-idf
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,507 @@
|
||||
#include "dali.h"
|
||||
#include "dali_hal.h"
|
||||
#include "dali_define.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <memory.h> // for memset
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
static SemaphoreHandle_t s_dali_core_lock;
|
||||
|
||||
static SemaphoreHandle_t dali_core_mutex(void)
|
||||
{
|
||||
if (s_dali_core_lock == NULL) {
|
||||
s_dali_core_lock = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
return s_dali_core_lock;
|
||||
}
|
||||
|
||||
static inline void dali_core_lock(void)
|
||||
{
|
||||
SemaphoreHandle_t mtx = dali_core_mutex();
|
||||
if (mtx) {
|
||||
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dali_core_unlock(void)
|
||||
{
|
||||
if (s_dali_core_lock) {
|
||||
xSemaphoreGiveRecursive(s_dali_core_lock);
|
||||
}
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new_generic(uint8_t bit_length, uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3) {
|
||||
Dali_msg_t dali_msg;
|
||||
dali_msg.id = 0;
|
||||
dali_msg.type = DALI_MSG_FORWARD;
|
||||
dali_msg.status = 0;
|
||||
dali_msg.length = bit_length; // bit count 1-32
|
||||
dali_msg.data[0] = address;
|
||||
dali_msg.data[1] = cmd1;
|
||||
dali_msg.data[2] = cmd2;
|
||||
dali_msg.data[3] = cmd3;
|
||||
return dali_msg;
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new(uint8_t address, uint8_t cmd1) {
|
||||
return dali_msg_new_generic(16, address, cmd1, 0, 0);
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new_1B(uint8_t data) {
|
||||
return dali_msg_new_generic(8, data, 0, 0, 0);
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new_2B(uint8_t address, uint8_t cmd1) {
|
||||
return dali_msg_new_generic(16, address, cmd1, 0, 0);
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new_3B(uint8_t address, uint8_t cmd1, uint8_t cmd2) {
|
||||
return dali_msg_new_generic(24, address, cmd1, cmd2, 0);
|
||||
}
|
||||
|
||||
Dali_msg_t dali_msg_new_4B(uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3) {
|
||||
return dali_msg_new_generic(32, address, cmd1, cmd2, cmd3);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
|
||||
// send message to DALI task - not expecting reply payload
|
||||
// TODO also restrict delay - to allow recovery
|
||||
static void dali_send_locked(Dali_msg_t *tx_msg) {
|
||||
xQueueSendToBack(dali_send_queue, tx_msg, portMAX_DELAY); // send message to DALI task
|
||||
xQueueReceive(dali_send_reply_queue, tx_msg, portMAX_DELAY); // always wait for reply that message was sent
|
||||
// TODO check status
|
||||
if(rx_debug_enabled & 0x01) {
|
||||
printf("** tm=%d[ms] st=%d len=%d d0=%d [0x%02X] d1=%d [0x%02X]\n",
|
||||
tx_msg->type, tx_msg->status, tx_msg->length, tx_msg->data[0], tx_msg->data[0], tx_msg->data[1], tx_msg->data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void dali_send(Dali_msg_t *tx_msg) {
|
||||
dali_core_lock();
|
||||
dali_send_locked(tx_msg);
|
||||
dali_core_unlock();
|
||||
}
|
||||
|
||||
// send double message
|
||||
void dali_send_double(Dali_msg_t *dali_msg) {
|
||||
dali_core_lock();
|
||||
dali_send_locked(dali_msg);
|
||||
// TODO check status
|
||||
dali_msg->id++; // increment message ID
|
||||
dali_delay_ms(10); // delay 13ms 101.8.1.2: 13.5 - 75ms
|
||||
dali_send_locked(dali_msg);
|
||||
// TODO check status
|
||||
dali_core_unlock();
|
||||
}
|
||||
|
||||
|
||||
int dali_query(Dali_msg_t *tx_msg, Dali_msg_t *rx_msg) {
|
||||
BaseType_t ret;
|
||||
|
||||
uint8_t bus_id = tx_msg ? tx_msg->id : 0;
|
||||
if (bus_id >= DALI_PHY_COUNT || dali_receive_queues[bus_id] == NULL) {
|
||||
bus_id = 0;
|
||||
}
|
||||
QueueHandle_t rx_q = dali_receive_queues[bus_id] ? dali_receive_queues[bus_id] : dali_receive_queue;
|
||||
if (rx_q == NULL) {
|
||||
printf("dali_query: rx queue not ready\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO check empty queue
|
||||
if(xQueueReceive(rx_q, rx_msg, 0) == pdTRUE) {
|
||||
printf("Queue not empty\n");
|
||||
return -1;
|
||||
}
|
||||
// printf("check A tx=%d tm=%d[ms] st=%d len=%d d0=0x%X\n", ret, tx_msg->type, tx_msg->status, tx_msg->length, tx_msg->data[0]);
|
||||
|
||||
dali_core_lock();
|
||||
dali_send_locked(tx_msg);
|
||||
// receive message from DALI task
|
||||
ret = xQueueReceive(rx_q, rx_msg, pdMS_TO_TICKS(50));
|
||||
dali_core_unlock();
|
||||
// printf("B rx=%d tm=%d[ms] st=%d len=%d d0=0x%X\n", ret, rx_msg->type, rx_msg->status, rx_msg->length, rx_msg->data[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
|
||||
// addr: 0-63
|
||||
uint32_t dali_cmd_query_rand_addr24(uint8_t addr)
|
||||
{
|
||||
Dali_msg_t tx, rx;
|
||||
uint32_t addr24 = 0;
|
||||
|
||||
uint8_t device = dali_short_address(addr);
|
||||
tx = dali_msg_new(device, DALI_CMD_QUERY_RAND_ADDR_H);
|
||||
if(dali_query(&tx, &rx) == pdTRUE) {
|
||||
addr24 |= rx.data[0] << 16;
|
||||
}
|
||||
else {
|
||||
printf("Cannot query long address H\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tx = dali_msg_new(device, DALI_CMD_QUERY_RAND_ADDR_M);
|
||||
if(dali_query(&tx, &rx) == pdTRUE) {
|
||||
addr24 |= rx.data[0] << 8;
|
||||
}
|
||||
else {
|
||||
printf("Cannot query long address M\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tx = dali_msg_new(device, DALI_CMD_QUERY_RAND_ADDR_L);
|
||||
if(dali_query(&tx, &rx) == pdTRUE) {
|
||||
addr24 |= rx.data[0];
|
||||
}
|
||||
else {
|
||||
printf("Cannot query long address L\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return addr24;
|
||||
}
|
||||
|
||||
// commands
|
||||
void dali_cmd(uint8_t device, uint8_t cmd)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(device, cmd);
|
||||
dali_send(&tx);
|
||||
}
|
||||
void dali_cmd_double(uint8_t device, uint8_t cmd)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(device, cmd);
|
||||
dali_send_double(&tx);
|
||||
}
|
||||
|
||||
|
||||
// device: 102.7.2.1 table 1
|
||||
void dali_cmd_off(uint8_t device)
|
||||
{
|
||||
dali_cmd(device, DALI_CMD_OFF);
|
||||
}
|
||||
|
||||
void dali_cmd_on(uint8_t device)
|
||||
{
|
||||
dali_cmd(device, DALI_CMD_ON);
|
||||
}
|
||||
|
||||
// this is broadcast command - all devices set DTR0
|
||||
void dali_set_dtr0(uint8_t value)
|
||||
{
|
||||
dali_cmd(DALI_CMD_DTR0, value);
|
||||
}
|
||||
|
||||
void dali_set_dtr1(uint8_t value)
|
||||
{
|
||||
dali_cmd(DALI_CMD_DTR1, value);
|
||||
}
|
||||
void dali_set_dtr2(uint8_t value)
|
||||
{
|
||||
dali_cmd(DALI_CMD_DTR2, value);
|
||||
}
|
||||
|
||||
// LEVELS
|
||||
void dali_set_level(uint8_t device, uint8_t cmd, uint8_t level)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
dali_set_dtr0(level);
|
||||
tx = dali_msg_new(device, cmd);
|
||||
dali_send_double(&tx);
|
||||
}
|
||||
// GROUP add or remove
|
||||
// group: 0-15
|
||||
void dali_group(uint8_t device1, uint8_t cmd, uint8_t group)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
cmd += group & 0x0F;
|
||||
tx = dali_msg_new(device1, cmd);
|
||||
dali_send_double(&tx);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// device address: 0-63
|
||||
|
||||
#define _QQ(cmd, val) \
|
||||
tx = dali_msg_new(device, cmd); \
|
||||
if(dali_query(&tx, &rx) == pdTRUE) { \
|
||||
val = rx.data[0]; \
|
||||
} \
|
||||
else printf("Cannot query: %s\n", #cmd); \
|
||||
|
||||
void dali_device_info(uint8_t addr)
|
||||
{
|
||||
Dali_msg_t tx, rx;
|
||||
// int ret;
|
||||
|
||||
uint8_t device = dali_short_address(addr);
|
||||
uint32_t addr24 = 0;
|
||||
uint8_t op=0, status=0, dtr0=0, dtr1=0, dtr2=0;
|
||||
uint8_t levelActual=0, levelMax=0, levelMin=0, levelPowerOn=0, levelSystemFail=0, levelPhysMin=0;
|
||||
uint8_t fadeTime=0, extFadeTime=0;
|
||||
|
||||
addr24 = dali_cmd_query_rand_addr24(addr);
|
||||
|
||||
_QQ(DALI_CMD_QUERY_OPERATING_MODE, op);
|
||||
_QQ(DALI_CMD_QUERY_STATUS, status);
|
||||
_QQ(DALI_CMD_QUERY_DTR0, dtr0);
|
||||
_QQ(DALI_CMD_QUERY_DTR1, dtr1);
|
||||
_QQ(DALI_CMD_QUERY_DTR2, dtr2);
|
||||
|
||||
printf("Device %2d [0x%zX]: MODE=0x%02X STAT=0x%02X DTR0=%3d [0x%02X] DTR1=%3d [0x%02X] DTR2=%3d [0x%02X]\n",
|
||||
addr, (size_t)addr24, op, status, dtr0, dtr0, dtr1, dtr1, dtr2, dtr2);
|
||||
|
||||
_QQ(DALI_CMD_QUERY_ACTUAL_LEVEL, levelActual);
|
||||
_QQ(DALI_CMD_QUERY_MAX_LEVEL, levelMax);
|
||||
_QQ(DALI_CMD_QUERY_MIN_LEVEL, levelMin);
|
||||
_QQ(DALI_CMD_QUERY_POWER_ON_LEVEL, levelPowerOn);
|
||||
_QQ(DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL, levelSystemFail);
|
||||
_QQ(DALI_CMD_QUERY_PHYSICAL_MIN, levelPhysMin);
|
||||
_QQ(DALI_CMD_QUERY_FADE_TIME, fadeTime);
|
||||
_QQ(DALI_CMD_QUERY_EXT_FADE_TIME, extFadeTime);
|
||||
|
||||
printf("* LEVELS ACT=%3d MAX=%3d MIN=%3d PMIN=%3d PWON=%3d SYSF=%3d FTM=0x%02X EFTM=0x%02X\n",
|
||||
levelActual, levelMax, levelMin, levelPhysMin, levelPowerOn, levelSystemFail, fadeTime, extFadeTime);
|
||||
|
||||
// groups
|
||||
uint8_t group0=0, group1=0;
|
||||
_QQ(DALI_CMD_QUERY_GROUPS_0_7, group0);
|
||||
_QQ(DALI_CMD_QUERY_GROUPS_8_15, group1);
|
||||
printf("* GROUPS 0-7=0x%02X 8-15=0x%02X\n", group0, group1);
|
||||
uint8_t scenes[16];
|
||||
for(int i=0; i<16; i++) {
|
||||
_QQ(DALI_CMD_QUERY_SCENE_LEVEL + i, scenes[i]);
|
||||
}
|
||||
printf("* SCENES: ");
|
||||
for(int i=0; i<16; i++) {
|
||||
printf("%3d ", scenes[i]);
|
||||
if(i % 8 == 7) printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
#undef _QQ // undefine macro
|
||||
|
||||
void dali_device_identify(uint8_t device)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(device, DALI_CMD_IDENTIFY);
|
||||
dali_send_double(&tx);
|
||||
}
|
||||
|
||||
// reset
|
||||
// - random 24-bit address to 0xFFFFFF
|
||||
// - levels to default values, clear groups and scenes
|
||||
void dali_device_reset(uint8_t device)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(device, DALI_CMD_RESET);
|
||||
dali_send_double(&tx);
|
||||
dali_delay_ms(300); // wait for reset
|
||||
}
|
||||
|
||||
|
||||
|
||||
// try identify all devices by asking for operating mode
|
||||
// - check from 0 to 63
|
||||
// - uint8 dev[64]
|
||||
void dali_find_all_short(uint8_t *dev, int with_print)
|
||||
{
|
||||
printf("Identifying all devices\n");
|
||||
Dali_msg_t tx, rx;
|
||||
int ret;
|
||||
memset(dev, 0, 64);
|
||||
int cnt = 0;
|
||||
for(int i=0; i<64; i++)
|
||||
{
|
||||
tx = dali_msg_new(dali_short_address(i), DALI_CMD_QUERY_OPERATING_MODE);
|
||||
ret = dali_query(&tx, &rx);
|
||||
if(ret == pdTRUE) {
|
||||
dev[i] = 1;
|
||||
cnt++;
|
||||
// printf("Device %2d: operating mode: 0x%X\n", i, rx.data[0]);
|
||||
if(with_print) printf("%d", i % 10);
|
||||
}
|
||||
else {
|
||||
if(with_print) printf(".");
|
||||
}
|
||||
if(with_print) {
|
||||
fflush(stdout);
|
||||
dali_delay_ms(100);
|
||||
}
|
||||
}
|
||||
if(with_print) printf(" \n");
|
||||
|
||||
printf("Found %d devices:", cnt);
|
||||
for(int i=0; i<64; i++) if(dev[i]) printf(" %d", i);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// RANDOMIZE
|
||||
// device: 0AAA AAA1 - only device with short address
|
||||
// 1111 1111 - all devices without short address
|
||||
// 0000 0000 - all devices
|
||||
void dali_cmd_randomize(uint8_t device)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
dali_cmd_initialise(device); // 0x00=ALL 0xFF=without short address 0b0AAA AAA1=with short address
|
||||
|
||||
// randomize 24-bit address
|
||||
tx = dali_msg_new(DALI_CMD_RANDOMISE, 0x00);
|
||||
dali_send_double(&tx);
|
||||
|
||||
dali_delay_ms(100); // wait for randomize
|
||||
dali_cmd_terminate();
|
||||
printf("*** Randomize end\n");
|
||||
}
|
||||
|
||||
// - requires INITIALISE state
|
||||
void dali_set_search_addr24(uint32_t addr24)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(DALI_CMD_SEARCHADDRH, addr24 >> 16);
|
||||
dali_send(&tx);
|
||||
tx = dali_msg_new(DALI_CMD_SEARCHADDRM, addr24 >> 8);
|
||||
dali_send(&tx);
|
||||
tx = dali_msg_new(DALI_CMD_SEARCHADDRL, addr24);
|
||||
dali_send(&tx);
|
||||
}
|
||||
|
||||
// only device in INITIALISE state and not in WITHDRAW state
|
||||
// return: 24-bit address
|
||||
// 0xFFFFFF - not found
|
||||
uint32_t dali_binary_search()
|
||||
{
|
||||
Dali_msg_t tx, rx;
|
||||
uint32_t high = 0xFFFFFF; // we must start from 0xFFFFFF but max address is 0xFFFFFE, it simplifies search
|
||||
uint32_t low = 0x000000;
|
||||
uint32_t mid;
|
||||
int ret;
|
||||
|
||||
for(int i=0; i<24; i++)
|
||||
{
|
||||
mid = (high + low) / 2;
|
||||
dali_set_search_addr24(mid);
|
||||
|
||||
// compare
|
||||
tx = dali_msg_new(DALI_CMD_COMPARE, 0x00);
|
||||
ret = dali_query(&tx, &rx);
|
||||
if(ret) high = mid; // found address <= mid and high is possible address
|
||||
else low = mid;
|
||||
|
||||
// printf("CMP i=%2d rx=%d tm=%d[ms] st=%d len=%d d0=0x%X mid=0x%06X high=0x%06X low=0x%06X\n",
|
||||
// i, ret, rx.type, rx.status, rx.length, rx.data[0], mid, high, low);
|
||||
|
||||
if(high - low < 2) {
|
||||
if(low == 0 && mid != 0) { // special case for 0
|
||||
mid = 0;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dali_delay_ms(10);
|
||||
}
|
||||
uint32_t addr24 = high; // found address is always in high
|
||||
printf("* Address: 0x%zX\n", (size_t)addr24);
|
||||
return addr24;
|
||||
}
|
||||
|
||||
// discard address in dali search command COMPARE
|
||||
// - requires INITIALISE state
|
||||
void dali_cmd_withdraw(uint32_t addr24)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
dali_set_search_addr24(addr24);
|
||||
|
||||
tx = dali_msg_new(DALI_CMD_WITHDRAW, 0x00);
|
||||
dali_send(&tx);
|
||||
printf("Withdraw 0x%06zX\n", (size_t)addr24);
|
||||
}
|
||||
|
||||
// initialise all devices selected by device:
|
||||
// device: 0AAA AAA1 - only device with this short address
|
||||
// 1111 1111 - all devices without short address
|
||||
// 0000 0000 - all devices
|
||||
void dali_cmd_initialise(uint8_t device)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(DALI_CMD_INITIALISE, device); // 0x00=ALL 0xFF=without short address 0b0AAA AAA1=with short address
|
||||
dali_send_double(&tx);
|
||||
}
|
||||
|
||||
void dali_cmd_terminate()
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
tx = dali_msg_new(DALI_CMD_TERMINATE, 0x00);
|
||||
dali_send(&tx);
|
||||
}
|
||||
|
||||
// Reassign short addresses to all devices selected by device:
|
||||
// - see dali_cmd_initialise(device)
|
||||
void dali_reindex_addresses(uint8_t device)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
uint32_t addr24 = 0;
|
||||
|
||||
printf("Start searching\n");
|
||||
dali_cmd_initialise(device); // 0x00=ALL 0xFF=without short address 0b0AAA AAA1=with short address
|
||||
|
||||
for(uint8_t i=0; i<63; i++)
|
||||
{
|
||||
addr24 = dali_binary_search();
|
||||
if(addr24 == 0xFFFFFF) break;
|
||||
uint8_t device = dali_short_address(i);
|
||||
|
||||
dali_set_search_addr24(addr24);
|
||||
tx = dali_msg_new(DALI_CMD_PROGRAM_SHORT, device);
|
||||
dali_send(&tx);
|
||||
|
||||
printf("Assign address: 0x%06zX --> %d\n", (size_t)addr24, i);
|
||||
dali_cmd_withdraw(addr24);
|
||||
}
|
||||
|
||||
dali_cmd_terminate();
|
||||
|
||||
printf("End searching\n");
|
||||
|
||||
}
|
||||
|
||||
void dali_change_short_address(int addr1, int addr2)
|
||||
{
|
||||
Dali_msg_t tx;
|
||||
uint8_t device1, device2;
|
||||
device1 = dali_short_address(addr1 & 0x3F);
|
||||
if(addr2 == -1) device2 = DALI_ADR_BROADCAST; // delete short address
|
||||
else if(addr2 < 0 || addr2 > 63) {
|
||||
printf("Invalid address: %d\n", addr2);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
device2 = dali_short_address(addr2 & 0x3F);
|
||||
}
|
||||
|
||||
int32_t addr24 = dali_cmd_query_rand_addr24(addr1);
|
||||
if(addr24 == -1) {
|
||||
printf("Cannot read address 24 for %d\n", addr1);
|
||||
return;
|
||||
}
|
||||
|
||||
dali_cmd_initialise(device1);
|
||||
|
||||
dali_set_search_addr24(addr24);
|
||||
tx = dali_msg_new(DALI_CMD_PROGRAM_SHORT, device2);
|
||||
dali_send(&tx);
|
||||
|
||||
dali_cmd_terminate();
|
||||
|
||||
printf("Address changed: %d --> %d\n", addr1, addr2);
|
||||
}
|
||||
@@ -0,0 +1,934 @@
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
//ESP-IDF HAL
|
||||
#ifdef CONFIG_IDF_TARGET
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/portmacro.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "driver/gptimer.h"
|
||||
|
||||
#include "dali_hal.h"
|
||||
|
||||
#ifndef CONFIG_DALI_API_QUEUE_LEN
|
||||
#define CONFIG_DALI_API_QUEUE_LEN 10
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_TX_QUEUE_LEN
|
||||
#define CONFIG_DALI_TX_QUEUE_LEN 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_TX_REPLY_QUEUE_LEN
|
||||
#define CONFIG_DALI_TX_REPLY_QUEUE_LEN 4
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_RX_QUEUE_LEN
|
||||
#define CONFIG_DALI_RX_QUEUE_LEN 50
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DEBUG_QUEUE_LEN
|
||||
#define CONFIG_DALI_DEBUG_QUEUE_LEN 100
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DALI_TASK_STACK_SIZE
|
||||
#define CONFIG_DALI_DALI_TASK_STACK_SIZE 2048
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DALI_TASK_PRIORITY
|
||||
#define CONFIG_DALI_DALI_TASK_PRIORITY 2
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DEBUG_TASK_STACK_SIZE
|
||||
#define CONFIG_DALI_DEBUG_TASK_STACK_SIZE 2048
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DALI_DEBUG_TASK_PRIORITY
|
||||
#define CONFIG_DALI_DEBUG_TASK_PRIORITY 1
|
||||
#endif
|
||||
|
||||
#define WITHIN_RANGE(x, min, max) ((x) > (min) && (x) < (max))
|
||||
#define MAX_DELTA_RELOAD_TIME 600000000 // 600s - max u32: 4,294,967,295~4,294s
|
||||
|
||||
#define DALI_BAUDRATE_MIN 400U
|
||||
#define DALI_BAUDRATE_MAX 2400U
|
||||
|
||||
typedef struct {
|
||||
uint32_t hb;
|
||||
uint32_t rx_hb_min;
|
||||
uint32_t rx_hb_max;
|
||||
uint32_t rx_2hb_min;
|
||||
uint32_t rx_2hb_max;
|
||||
uint32_t rx_stop_cond;
|
||||
uint32_t time_bus_down;
|
||||
uint32_t time_break_min;
|
||||
uint32_t time_recovery_min;
|
||||
uint32_t tx_stop_cond;
|
||||
uint32_t collision_txrx_delta;
|
||||
} dali_timing_t;
|
||||
|
||||
static const char *TAG = "dali_hal";
|
||||
|
||||
typedef struct {
|
||||
uint8_t bus_id;
|
||||
uint8_t tx_pin;
|
||||
uint8_t rx_pin;
|
||||
|
||||
dali_tx_state_t tx_state;
|
||||
dali_rx_state_t rx_state;
|
||||
dali_bus_state_t bus_state;
|
||||
|
||||
uint64_t rx_last_edge_time;
|
||||
uint64_t tx_last_edge_time;
|
||||
|
||||
uint32_t rx_tx_delta;
|
||||
uint32_t rx_pulse_width;
|
||||
uint8_t rx_level;
|
||||
|
||||
Dali_msg_t tx_data;
|
||||
Dali_msg_t rx_data;
|
||||
|
||||
uint8_t tx_half_bit_counter;
|
||||
uint8_t tx_data_bit_counter;
|
||||
|
||||
uint8_t rx_half_bit_counter;
|
||||
uint8_t rx_data_bit_counter;
|
||||
|
||||
QueueHandle_t tx_queue;
|
||||
QueueHandle_t tx_reply_queue;
|
||||
QueueHandle_t rx_queue;
|
||||
bool inited;
|
||||
} dali_bus_ctx_t;
|
||||
|
||||
static volatile dali_timing_t s_timing;
|
||||
static uint32_t s_baudrate = DALI_DEFAULT_BAUDRATE;
|
||||
static bool s_timing_ready = false;
|
||||
static portMUX_TYPE s_timing_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static SemaphoreHandle_t s_hal_mutex;
|
||||
|
||||
static gptimer_handle_t gptimer = NULL;
|
||||
static SemaphoreHandle_t dali_hal_mutex(void)
|
||||
{
|
||||
if (s_hal_mutex == NULL) {
|
||||
s_hal_mutex = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
return s_hal_mutex;
|
||||
}
|
||||
|
||||
static inline void dali_hal_lock(void)
|
||||
{
|
||||
SemaphoreHandle_t mtx = dali_hal_mutex();
|
||||
if (mtx) {
|
||||
xSemaphoreTakeRecursive(mtx, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dali_hal_unlock(void)
|
||||
{
|
||||
if (s_hal_mutex) {
|
||||
xSemaphoreGiveRecursive(s_hal_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t dali_half_bit_from_baud(uint32_t baudrate)
|
||||
{
|
||||
if (baudrate < DALI_BAUDRATE_MIN || baudrate > DALI_BAUDRATE_MAX) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t hb = 500000ULL + (baudrate / 2U); // round to nearest
|
||||
hb /= baudrate;
|
||||
if (hb == 0 || hb > 2000000ULL) { // should never happen with checked bounds
|
||||
return 0;
|
||||
}
|
||||
return (uint32_t)hb;
|
||||
}
|
||||
|
||||
static inline uint32_t scale_time_by_hb(uint32_t base_us, uint32_t hb_us)
|
||||
{
|
||||
return (uint32_t)(((uint64_t)base_us * hb_us + (DALI_TIME_HB / 2U)) / DALI_TIME_HB);
|
||||
}
|
||||
|
||||
static esp_err_t update_timing_locked(uint32_t baudrate)
|
||||
{
|
||||
uint32_t hb = dali_half_bit_from_baud(baudrate);
|
||||
if (hb == 0) {
|
||||
ESP_LOGE(TAG, "invalid baudrate: %u", baudrate);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
dali_timing_t new_timing = {
|
||||
.hb = hb,
|
||||
.rx_hb_min = scale_time_by_hb(DALI_RX_HB_MIN, hb),
|
||||
.rx_hb_max = scale_time_by_hb(DALI_RX_HB_MAX, hb),
|
||||
.rx_2hb_min = scale_time_by_hb(DALI_RX_2HB_MIN, hb),
|
||||
.rx_2hb_max = scale_time_by_hb(DALI_RX_2HB_MAX, hb),
|
||||
.rx_stop_cond = scale_time_by_hb(DALI_RX_STOP_COND, hb),
|
||||
.time_bus_down = scale_time_by_hb(DALI_TIME_BUS_DOWN, hb),
|
||||
.time_break_min = scale_time_by_hb(DALI_TIME_BREAK_MIN, hb),
|
||||
.time_recovery_min = scale_time_by_hb(DALI_TIME_RECOVERY_MIN, hb),
|
||||
.tx_stop_cond = scale_time_by_hb(DALI_TX_STOP_COND, hb),
|
||||
.collision_txrx_delta = scale_time_by_hb(DALI_COLLISION_TXRX_DELTA, hb),
|
||||
};
|
||||
|
||||
portENTER_CRITICAL(&s_timing_spinlock);
|
||||
s_timing = new_timing;
|
||||
s_baudrate = baudrate;
|
||||
s_timing_ready = true;
|
||||
portEXIT_CRITICAL(&s_timing_spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t ensure_timing_ready_locked(void)
|
||||
{
|
||||
if (s_timing_ready) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return update_timing_locked(s_baudrate);
|
||||
}
|
||||
|
||||
static esp_err_t apply_timer_alarm_locked(void)
|
||||
{
|
||||
if (!gptimer) {
|
||||
return ESP_OK;
|
||||
}
|
||||
gptimer_alarm_config_t timer_alarm_config = {
|
||||
.alarm_count = s_timing.hb,
|
||||
.reload_count = 0,
|
||||
.flags = {
|
||||
.auto_reload_on_alarm = true,
|
||||
},
|
||||
};
|
||||
return gptimer_set_alarm_action(gptimer, &timer_alarm_config);
|
||||
}
|
||||
|
||||
// public queue for sending and receiving data
|
||||
QueueHandle_t dali_send_queue; // [Dali_msg_t] from other tasks to dali task
|
||||
QueueHandle_t dali_send_reply_queue; // [Dali_msg_t] from dali task to other tasks
|
||||
QueueHandle_t dali_receive_queue; // alias to bus 0 for compatibility
|
||||
QueueHandle_t dali_receive_queues[DALI_PHY_COUNT];
|
||||
|
||||
// internal queues for debug data
|
||||
struct Dali_rx_dbg_data {
|
||||
uint32_t rx_pulse_width;
|
||||
uint32_t rx_tx_delta;
|
||||
uint8_t level;
|
||||
uint8_t bus_id;
|
||||
};
|
||||
typedef struct Dali_rx_dbg_data Dali_rx_dbg_data_t;
|
||||
static QueueHandle_t rx_dbg_queue; // [Dali_rx_dbg_data_t] from GPIO ISR to debug task, data:
|
||||
uint8_t rx_debug_enabled = 0; // enable debug data - set to 1 in main.c
|
||||
|
||||
static dali_bus_ctx_t s_bus[DALI_PHY_COUNT];
|
||||
static bool s_timer_started = false;
|
||||
static bool s_timer_configured = false;
|
||||
static bool s_timer_enabled = false;
|
||||
#if CONFIG_DALI_ENABLE_DEBUG_TASK
|
||||
static bool s_debug_task_created = false;
|
||||
#endif
|
||||
static bool s_dali_task_created = false;
|
||||
|
||||
#define DALI_SET_BUS_HIGH(bus) gpio_set_level((bus)->tx_pin, DALI_TX_HIGH) // set bus level
|
||||
#define DALI_SET_BUS_LOW(bus) gpio_set_level((bus)->tx_pin, DALI_TX_LOW) // set bus level
|
||||
#define DALI_SET_BUS_LEVEL(bus, x) gpio_set_level((bus)->tx_pin, ((x)==DALI_TX_HIGH)) // set bus level
|
||||
|
||||
// !!! read from RX pin, we need real bus level, not logic level of TX pin
|
||||
// return: 0 - bus level low, active state
|
||||
// 1 - bus level high, idle state
|
||||
#define DALI_GET_BUS_LEVEL(bus) (gpio_get_level((bus)->rx_pin) == (DALI_RX_HIGH)) // get bus level
|
||||
// return: 0 - tx pin drive bus low, active state
|
||||
// 1 - tx pin drive bus high, idle state
|
||||
#define DALI_GET_TX_LEVEL(bus) (gpio_get_level((bus)->tx_pin) == (DALI_TX_HIGH)) // get TX pin level
|
||||
|
||||
static inline bool bus_valid(uint8_t bus_id) {
|
||||
return bus_id < DALI_PHY_COUNT && s_bus[bus_id].inited;
|
||||
}
|
||||
|
||||
// GPIO ISR handler
|
||||
// define rx_gpio_isr_handler on any edge
|
||||
static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
|
||||
{
|
||||
dali_bus_ctx_t *bus = (dali_bus_ctx_t *)arg;
|
||||
if (bus == NULL || !bus->inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t rx_current_edge_time = esp_timer_get_time(); // get time in us
|
||||
uint8_t rx_previous_level = bus->rx_level;
|
||||
|
||||
Dali_rx_dbg_data_t dbg;
|
||||
|
||||
// rx_level = 1 if and only if DALI bus is really high, idle
|
||||
bus->rx_level = DALI_GET_BUS_LEVEL(bus); // get level of RX pin - not depend on hw: 0 - low, 1 - high
|
||||
|
||||
bus->rx_pulse_width = rx_current_edge_time - bus->rx_last_edge_time; // time from last edge
|
||||
bus->rx_tx_delta = rx_current_edge_time - bus->tx_last_edge_time; // time from last edge
|
||||
|
||||
// always save time of last edge
|
||||
bus->rx_last_edge_time = rx_current_edge_time; // get time in us
|
||||
|
||||
if(bus->bus_state == DALI_BUS_READY && bus->rx_level == 0) // found start bit
|
||||
{
|
||||
// within range for backward frame
|
||||
uint32_t time_ms = bus->rx_pulse_width / 1000; // 1ms = 1000us
|
||||
if(time_ms>255) bus->rx_data.type = 255;
|
||||
else bus->rx_data.type = time_ms; // pulse width in ms for later identification of type msg
|
||||
|
||||
bus->rx_data.id = bus->bus_id; // store bus id
|
||||
bus->rx_data.length = 0;
|
||||
bus->rx_data.status = DALI_FRAME_UNKNOWN; // on start in unknown state
|
||||
memset(bus->rx_data.data, 0, DALI_MAX_BYTES); // clear data
|
||||
|
||||
bus->rx_state = RX_STATE_START; // start receiving
|
||||
bus->bus_state = DALI_BUS_RECEIVING; // bus is receiving
|
||||
bus->rx_half_bit_counter = 0;
|
||||
bus->rx_data_bit_counter = 0; // actually received bits count
|
||||
}
|
||||
else if(bus->bus_state == DALI_BUS_RECEIVING)
|
||||
{
|
||||
if(bus->rx_state == RX_STATE_START) {
|
||||
// start bit always has width HB and level 1
|
||||
if(bus->rx_level == 1 && WITHIN_RANGE(bus->rx_pulse_width, s_timing.rx_hb_min, s_timing.rx_hb_max)) {
|
||||
bus->rx_state = RX_STATE_DATA; // start receiving data
|
||||
bus->rx_half_bit_counter++;
|
||||
}
|
||||
else {
|
||||
bus->bus_state = DALI_BUS_ERROR; // not in range
|
||||
bus->rx_data.status = DALI_FRAME_TIME_VIOLATION;
|
||||
goto end_rx_isr;
|
||||
}
|
||||
}
|
||||
else if(bus->rx_state == RX_STATE_DATA) {
|
||||
// for long gap there are two valid cases:
|
||||
// 1. previus_level == 1 and current_level == 0 then result is 0 bit
|
||||
// 2. previus_level == 0 and current_level == 1 then result is 1 bit
|
||||
|
||||
// if for whatever reason we get the same level as previous we are in error, eg we missed edge
|
||||
if(rx_previous_level == bus->rx_level) {
|
||||
bus->rx_state = RX_STATE_ERROR; // invalid bit
|
||||
bus->rx_data.status = DALI_FRAME_SEQUENCE_ERROR;
|
||||
goto end_rx_isr;
|
||||
}
|
||||
|
||||
if(WITHIN_RANGE(bus->rx_pulse_width, s_timing.rx_2hb_min, s_timing.rx_2hb_max))
|
||||
{
|
||||
if((bus->rx_half_bit_counter & 0x01) == 0) { // even half bit
|
||||
bus->rx_state = RX_STATE_ERROR; // invalid bit
|
||||
goto end_rx_isr;
|
||||
}
|
||||
if(rx_previous_level == 1) { // edge:1->0 value 0, skip because data[] is already set to 0
|
||||
}
|
||||
else { // edge:0->1 value 1
|
||||
if(bus->rx_level == 1) { // result to: 1
|
||||
bus->rx_data.data[bus->rx_data_bit_counter/8] |= (1 << (7 - (bus->rx_data_bit_counter % 8))); // set bit
|
||||
}
|
||||
}
|
||||
bus->rx_half_bit_counter+=2; // 3,5,7...
|
||||
bus->rx_data_bit_counter++; // increment bit counter
|
||||
}
|
||||
else if(WITHIN_RANGE(bus->rx_pulse_width, s_timing.rx_hb_min, s_timing.rx_hb_max))
|
||||
{
|
||||
|
||||
if(bus->rx_half_bit_counter & 0x01) { // first half of bit
|
||||
bus->rx_half_bit_counter++;
|
||||
}
|
||||
else { // second half of bit
|
||||
bus->rx_half_bit_counter++;
|
||||
if(rx_previous_level == 1) { // edge:1->0 value 0 - skip because data[] is already set to 0
|
||||
}
|
||||
else { // edge:0->1 value 1
|
||||
if(bus->rx_level == 1) { // 0->1 is 1
|
||||
bus->rx_data.data[bus->rx_data_bit_counter/8] |= (1 << (7 - (bus->rx_data_bit_counter % 8))); // set bit
|
||||
}
|
||||
}
|
||||
bus->rx_data_bit_counter++; // increment bit counter - only on second half
|
||||
}
|
||||
if(bus->rx_data_bit_counter >= DALI_MAX_BITS) {
|
||||
bus->rx_state = RX_STATE_STOP; // stop receiving - we cannot receive more bits
|
||||
}
|
||||
}
|
||||
else {
|
||||
bus->rx_data.status = DALI_FRAME_TIME_VIOLATION;
|
||||
bus->rx_state = RX_STATE_ERROR; // invalid bit
|
||||
goto end_rx_isr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if collision detected: we are too late after bit was transmitted
|
||||
else if (bus->bus_state == DALI_BUS_TRANSMITTING && bus->rx_tx_delta > s_timing.collision_txrx_delta)
|
||||
{
|
||||
// we need now to start collision recovery with time break: 101.9.2.4
|
||||
DALI_SET_BUS_LOW(bus); // force TX low - active state, inform about collision, this also generate new GPIO ISR
|
||||
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
bus->bus_state = DALI_BUS_TIME_BREAK; // we are in time break state
|
||||
bus->tx_data.status = DALI_FRAME_COLLISION; // collision detected
|
||||
}
|
||||
else if (bus->bus_state == DALI_BUS_RECOVERY)
|
||||
{ // we should not receive data during recover
|
||||
bus->bus_state = DALI_BUS_ERROR; // not idle wait for idle state or cancel transmission and start receiving
|
||||
}
|
||||
|
||||
end_rx_isr:
|
||||
|
||||
if(rx_debug_enabled & 0x02) {
|
||||
dbg.level = bus->rx_level;
|
||||
dbg.rx_pulse_width = bus->rx_pulse_width;
|
||||
dbg.rx_tx_delta = bus->rx_tx_delta;
|
||||
dbg.bus_id = bus->bus_id;
|
||||
xQueueSendToBackFromISR(rx_dbg_queue, &dbg, NULL); // send data to queue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
// HW TIMER ISR
|
||||
// is triggered at interval of 416us (half bit)
|
||||
static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
|
||||
{
|
||||
BaseType_t yield = false;
|
||||
uint32_t rx_delta = time_now - bus->rx_last_edge_time; // time from last edge
|
||||
uint32_t tx_delta = time_now - bus->tx_last_edge_time; // time from last edge
|
||||
|
||||
if(rx_delta > MAX_DELTA_RELOAD_TIME)
|
||||
{
|
||||
bus->rx_last_edge_time = time_now - MAX_DELTA_RELOAD_TIME/2; // half of max time
|
||||
}
|
||||
if(tx_delta > MAX_DELTA_RELOAD_TIME)
|
||||
{
|
||||
bus->tx_last_edge_time = time_now - MAX_DELTA_RELOAD_TIME/2; // half of max time
|
||||
}
|
||||
|
||||
// recovery from different error states: UNKNOWN, ERROR, POWER_DOWN
|
||||
if(bus->bus_state <= DALI_BUS_ERROR)
|
||||
{ // 101.8.2.4 - startup BUS after 2.4ms
|
||||
if(bus->rx_level==1 && rx_delta > s_timing.rx_stop_cond)
|
||||
{
|
||||
bus->bus_state = DALI_BUS_READY; // bus is ready
|
||||
}
|
||||
}
|
||||
// if bus power down - if bus is low for more then 45ms
|
||||
if(bus->rx_level==0 && rx_delta > s_timing.time_bus_down)
|
||||
{ // power lost
|
||||
bus->bus_state = DALI_BUS_POWER_DOWN; // bus is power down - recovery see previous if
|
||||
DALI_SET_BUS_HIGH(bus); // make sure TX is high
|
||||
|
||||
}
|
||||
// recovery from collision detection
|
||||
// if end of TIME BREAK BUS is LOW - collision recovery: 101.9.2.4 tab 25
|
||||
// BUS: IDLE, HIGH - we are last device who keep bus low, if we want to restart transmission wait for TIME RECOVERY
|
||||
// BUS: ACTIVE, LOW - BUS is busy, let caller to restart transmission
|
||||
if(bus->bus_state == DALI_BUS_TIME_BREAK && rx_delta > s_timing.time_break_min)
|
||||
{
|
||||
DALI_SET_BUS_HIGH(bus); // TX high - idle state - generate ISR on RX pin
|
||||
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
// read bus state
|
||||
if(DALI_GET_BUS_LEVEL(bus) == 0) // other device is keeping bus low
|
||||
{
|
||||
// wait for bus to be idle before starting recovery - caller will restart transmission
|
||||
bus->bus_state = DALI_BUS_ERROR; // bus is in multi master state: 101.9.2.4 Fig 17
|
||||
}
|
||||
else
|
||||
{
|
||||
// bus is free - we are the last one who keeps bus low, we can start recovery
|
||||
bus->bus_state = DALI_BUS_RECOVERY; // start recovery for 4ms: 101.9.2.4 tab 25
|
||||
// TODO: add check if we want to restart transmission !!!
|
||||
}
|
||||
}
|
||||
else if(bus->bus_state == DALI_BUS_RECOVERY && rx_delta > s_timing.time_recovery_min)
|
||||
{
|
||||
bus->bus_state = DALI_BUS_READY; // bus is ready
|
||||
// immediately start transmitting if we have data
|
||||
}
|
||||
|
||||
// start transmitting
|
||||
else if(bus->bus_state == DALI_BUS_READY && bus->tx_state == TX_STATE_IDLE && bus->tx_queue && xQueueReceiveFromISR(bus->tx_queue, &bus->tx_data, NULL) == pdTRUE)
|
||||
{
|
||||
bus->tx_data.status = DALI_FRAME_ERROR; // error status - will be set ok on success
|
||||
bus->bus_state = DALI_BUS_TRANSMITTING; // bus is transmitting
|
||||
bus->tx_state = TX_STATE_START; // start transmitting
|
||||
bus->tx_half_bit_counter = 0;
|
||||
bus->tx_data_bit_counter = 0; // actually sent bits count
|
||||
DALI_SET_BUS_LOW(bus); // start bit first half
|
||||
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
}
|
||||
else if(bus->bus_state == DALI_BUS_TRANSMITTING)
|
||||
{
|
||||
// transmit data
|
||||
if(bus->tx_state == TX_STATE_START) {
|
||||
bus->tx_state = TX_STATE_DATA; // start transmitting data
|
||||
bus->tx_half_bit_counter++;
|
||||
DALI_SET_BUS_HIGH(bus); // start bit second half
|
||||
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
}
|
||||
else if(bus->tx_state == TX_STATE_DATA) {
|
||||
bool value = (bus->tx_data.data[bus->tx_data_bit_counter/8] >> ( 7 - (bus->tx_data_bit_counter % 8) )) & 0x01;
|
||||
value ^= bus->tx_half_bit_counter & 0x01; // xor=invert value for odd half bit 1:0->1 and 0:1->0
|
||||
DALI_SET_BUS_LEVEL(bus, value);
|
||||
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
bus->tx_half_bit_counter++; // increment half bit counter before next test
|
||||
if(bus->tx_half_bit_counter & 0x01) { // next bit
|
||||
bus->tx_data_bit_counter++;
|
||||
if(bus->tx_data_bit_counter >= bus->tx_data.length) {
|
||||
bus->tx_state = TX_STATE_STOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(bus->tx_state == TX_STATE_STOP) {
|
||||
// here we check TX (NOT RX) bit state
|
||||
if(DALI_GET_TX_LEVEL(bus) == 0) // really ok - otherwise we will keep bus low forever
|
||||
{
|
||||
DALI_SET_BUS_HIGH(bus);
|
||||
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
|
||||
}
|
||||
else if(tx_delta > s_timing.tx_stop_cond) {
|
||||
bus->tx_data.status = DALI_FRAME_OK; // frame is OK
|
||||
if (bus->tx_reply_queue) {
|
||||
xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, &yield); // send data to queue
|
||||
}
|
||||
bus->tx_state = TX_STATE_IDLE; // final state with transmitted data
|
||||
bus->bus_state = DALI_BUS_READY; // bus is ready
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(bus->bus_state == DALI_BUS_READY && bus->tx_state > TX_STATE_IDLE)
|
||||
{
|
||||
// we are not transmitting but we have data - reply to queue and let error state in tx_data.status
|
||||
if (bus->tx_reply_queue) {
|
||||
xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, &yield); // send data to queue
|
||||
}
|
||||
bus->tx_state = TX_STATE_IDLE; // clear state
|
||||
}
|
||||
|
||||
// recover receiving
|
||||
if(bus->bus_state == DALI_BUS_RECEIVING) {
|
||||
if(bus->rx_state == RX_STATE_ERROR) {
|
||||
// wait until bus is idle
|
||||
if(rx_delta > s_timing.rx_stop_cond) { // minimum time 2400us
|
||||
bus->rx_state = RX_STATE_IDLE;
|
||||
bus->bus_state = DALI_BUS_READY;
|
||||
// rx_data.status = DALI_FRAME_ERROR; // should be set inside ISR
|
||||
bus->rx_data.length = bus->rx_data_bit_counter; // set length of data
|
||||
// rx_data.data[0] = 0xAA; // debug
|
||||
if (bus->rx_queue) {
|
||||
xQueueSendToBackFromISR(bus->rx_queue, &bus->rx_data, &yield); // send data to queue
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(bus->rx_state == RX_STATE_DATA || bus->rx_state == RX_STATE_STOP) {
|
||||
// wait until bus is idle
|
||||
if(rx_delta > s_timing.rx_stop_cond) { // minimum time 2400us
|
||||
bus->rx_state = RX_STATE_IDLE;
|
||||
bus->bus_state = DALI_BUS_READY;
|
||||
bus->rx_data.status = DALI_FRAME_OK; // frame is OK
|
||||
bus->rx_data.length = bus->rx_data_bit_counter; // set length of data
|
||||
// rx_data.data[0] = 0xBB; // debug
|
||||
if (bus->rx_queue) {
|
||||
xQueueSendToBackFromISR(bus->rx_queue, &bus->rx_data, &yield); // send data to queue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return yield;
|
||||
}
|
||||
|
||||
// HW TIMER ISR wrapper
|
||||
bool IRAM_ATTR hw_timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *arg)
|
||||
{
|
||||
bool yield = false;
|
||||
uint64_t time_now = esp_timer_get_time();
|
||||
for (int i = 0; i < DALI_PHY_COUNT; i++) {
|
||||
if (s_bus[i].inited) {
|
||||
yield |= handle_bus_timer(&s_bus[i], time_now);
|
||||
}
|
||||
}
|
||||
return yield;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// should be at lowest priority
|
||||
#if CONFIG_DALI_ENABLE_DEBUG_TASK
|
||||
static void debug_task(void *pvParameters)
|
||||
{
|
||||
Dali_rx_dbg_data_t dbg;
|
||||
static uint8_t i=0;
|
||||
static uint8_t HB=0; // half bit counter
|
||||
char v = '*'; // interpret value
|
||||
while (1)
|
||||
{
|
||||
if(xQueueReceive(rx_dbg_queue, &dbg, portMAX_DELAY) == pdTRUE) // wait forever
|
||||
{
|
||||
if(dbg.rx_pulse_width > 1000) { i=0; HB=0; v='*';}
|
||||
else if ((HB & 0x01) == 1) {
|
||||
if(HB == 2) v = 'S';
|
||||
else v = '0' + dbg.level;
|
||||
}
|
||||
else v=' ';
|
||||
printf("bus[%u] rx: [%2d] pw=%lu v=%u rtd=%lu [%c]",
|
||||
dbg.bus_id, i++, dbg.rx_pulse_width, dbg.level, dbg.rx_tx_delta, v);
|
||||
if(dbg.rx_pulse_width < 1000) {
|
||||
if(dbg.rx_pulse_width > 550) HB+=2;
|
||||
else HB++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t ensure_isr_service(void)
|
||||
{
|
||||
static bool installed = false;
|
||||
if (installed) {
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t err = gpio_install_isr_service(0);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
installed = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
installed = true;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ensure_common_queues(void)
|
||||
{
|
||||
if (!dali_send_queue) {
|
||||
dali_send_queue = xQueueCreate(CONFIG_DALI_API_QUEUE_LEN, sizeof(Dali_msg_t));
|
||||
}
|
||||
if (!dali_send_reply_queue) {
|
||||
dali_send_reply_queue = xQueueCreate(CONFIG_DALI_API_QUEUE_LEN, sizeof(Dali_msg_t));
|
||||
}
|
||||
if (!rx_dbg_queue) {
|
||||
rx_dbg_queue = xQueueCreate(CONFIG_DALI_DEBUG_QUEUE_LEN, sizeof(Dali_rx_dbg_data_t));
|
||||
}
|
||||
#if CONFIG_DALI_ENABLE_DEBUG_TASK
|
||||
if (!s_debug_task_created) {
|
||||
xTaskCreate(debug_task, "debug_task", CONFIG_DALI_DEBUG_TASK_STACK_SIZE, NULL, CONFIG_DALI_DEBUG_TASK_PRIORITY, NULL); // at low priority !!!
|
||||
s_debug_task_created = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t ensure_timer_started(void)
|
||||
{
|
||||
dali_hal_lock();
|
||||
if (s_timer_started) {
|
||||
dali_hal_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = ensure_timing_ready_locked();
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
if (gptimer == NULL) {
|
||||
gptimer_config_t timer_config = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.intr_priority = 0,
|
||||
.flags = {
|
||||
.intr_shared = true,
|
||||
},
|
||||
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
|
||||
};
|
||||
err = gptimer_new_timer(&timer_config, &gptimer);
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s_timer_configured) {
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = hw_timer_callback,
|
||||
};
|
||||
err = gptimer_register_event_callbacks(gptimer, &cbs, NULL);
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
s_timer_configured = true;
|
||||
}
|
||||
|
||||
err = apply_timer_alarm_locked();
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!s_timer_enabled) {
|
||||
err = gptimer_enable(gptimer);
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
s_timer_enabled = true;
|
||||
}
|
||||
|
||||
err = gptimer_start(gptimer);
|
||||
if (err != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
s_timer_started = true;
|
||||
dali_hal_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t init_bus(uint8_t bus_id, uint8_t tx_pin, uint8_t rx_pin)
|
||||
{
|
||||
if (bus_id >= DALI_PHY_COUNT) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
dali_bus_ctx_t *bus = &s_bus[bus_id];
|
||||
if (bus->inited) {
|
||||
if (bus->tx_pin == tx_pin && bus->rx_pin == rx_pin) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ensure_common_queues();
|
||||
|
||||
bus->bus_id = bus_id;
|
||||
bus->tx_pin = tx_pin;
|
||||
bus->rx_pin = rx_pin;
|
||||
|
||||
gpio_config_t io_conf;
|
||||
|
||||
// DALI TX
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = BIT(bus->tx_pin);
|
||||
io_conf.pull_down_en = 0;
|
||||
io_conf.pull_up_en = 0;
|
||||
err = gpio_config(&io_conf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// DALI RX
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = BIT(bus->rx_pin);
|
||||
io_conf.pull_down_en = 0;
|
||||
io_conf.pull_up_en = 0; // no pull-up or pull-down - bus has default pull-down
|
||||
err = gpio_config(&io_conf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// set initial state
|
||||
DALI_SET_BUS_HIGH(bus); // TX high - idle state
|
||||
|
||||
if (!bus->tx_queue) {
|
||||
bus->tx_queue = xQueueCreate(CONFIG_DALI_TX_QUEUE_LEN, sizeof(Dali_msg_t));
|
||||
}
|
||||
if (!bus->tx_reply_queue) {
|
||||
bus->tx_reply_queue = xQueueCreate(CONFIG_DALI_TX_REPLY_QUEUE_LEN, sizeof(Dali_msg_t));
|
||||
}
|
||||
if (!bus->rx_queue) {
|
||||
bus->rx_queue = xQueueCreate(CONFIG_DALI_RX_QUEUE_LEN, sizeof(Dali_msg_t));
|
||||
dali_receive_queues[bus_id] = bus->rx_queue;
|
||||
if (bus_id == 0) {
|
||||
dali_receive_queue = bus->rx_queue; // backward compatibility
|
||||
}
|
||||
}
|
||||
|
||||
bus->rx_last_edge_time = esp_timer_get_time(); // get time in us - startup time
|
||||
bus->rx_level = DALI_GET_BUS_LEVEL(bus); // get level of RX pin
|
||||
bus->tx_last_edge_time = bus->rx_last_edge_time;
|
||||
|
||||
err = ensure_isr_service();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = gpio_set_intr_type(bus->rx_pin, GPIO_INTR_ANYEDGE);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = gpio_isr_handler_add(bus->rx_pin, rx_gpio_isr_handler, bus); // add isr handler for specific gpio pin
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
bus->bus_state = DALI_BUS_READY;
|
||||
bus->tx_state = TX_STATE_IDLE;
|
||||
bus->rx_state = RX_STATE_IDLE;
|
||||
|
||||
bus->inited = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t dali_init_ports(uint8_t _dali_tx_pin, uint8_t _dali_rx_pin)
|
||||
{
|
||||
return dali_hal_init(0, _dali_tx_pin, _dali_rx_pin);
|
||||
}
|
||||
|
||||
// communication with ISR - send data to queue and wait for reply
|
||||
static int dali_tx_bus(dali_bus_ctx_t *bus, Dali_msg_t *dali_msg)
|
||||
{
|
||||
if (bus == NULL || !bus->inited) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if(xQueueSendToBack(bus->tx_queue, dali_msg, pdMS_TO_TICKS(50)) == pdFALSE) {
|
||||
xQueueReset(bus->tx_queue); // clear queue
|
||||
printf("dali_tx: Queue full\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if(xQueueReceive(bus->tx_reply_queue, dali_msg, pdMS_TO_TICKS(50)) == pdFALSE) {
|
||||
xQueueReset(bus->tx_reply_queue); // clear queue
|
||||
printf("dali_tx: No reply\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// dali_task - should run at highest priority
|
||||
void dali_task(void *pvParameters)
|
||||
{
|
||||
Dali_msg_t dali_msg;
|
||||
while (1)
|
||||
{
|
||||
// data from queue -> copy to local variable
|
||||
if(xQueueReceive(dali_send_queue, &dali_msg, portMAX_DELAY) == pdTRUE) {
|
||||
uint8_t bus_id = dali_msg.id;
|
||||
if (!bus_valid(bus_id)) {
|
||||
bus_id = 0;
|
||||
}
|
||||
dali_msg.id = bus_id;
|
||||
dali_bus_ctx_t *bus = &s_bus[bus_id];
|
||||
dali_tx_bus(bus, &dali_msg);
|
||||
// send data to queue
|
||||
xQueueSendToBack(dali_send_reply_queue, &dali_msg, 0);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t dali_hal_init(uint8_t dali_id, uint8_t tx_pin, uint8_t rx_pin)
|
||||
{
|
||||
dali_hal_lock();
|
||||
|
||||
esp_err_t err = ensure_timing_ready_locked();
|
||||
if (err == ESP_OK) {
|
||||
ensure_common_queues();
|
||||
|
||||
if (!s_dali_task_created) {
|
||||
BaseType_t created = xTaskCreate(dali_task, "dali_task", CONFIG_DALI_DALI_TASK_STACK_SIZE, NULL, CONFIG_DALI_DALI_TASK_PRIORITY, NULL); // at high priority
|
||||
if (created != pdPASS) {
|
||||
err = ESP_FAIL;
|
||||
} else {
|
||||
s_dali_task_created = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err == ESP_OK) {
|
||||
err = init_bus(dali_id, tx_pin, rx_pin);
|
||||
}
|
||||
|
||||
if (err == ESP_OK) {
|
||||
err = ensure_timer_started();
|
||||
}
|
||||
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t dali_hal_set_baudrate(uint32_t baudrate)
|
||||
{
|
||||
dali_hal_lock();
|
||||
|
||||
bool resume_timer = s_timer_started && gptimer;
|
||||
if (resume_timer) {
|
||||
esp_err_t stop_ret = gptimer_stop(gptimer);
|
||||
if (stop_ret != ESP_OK) {
|
||||
dali_hal_unlock();
|
||||
return stop_ret;
|
||||
}
|
||||
s_timer_started = false;
|
||||
}
|
||||
|
||||
esp_err_t err = update_timing_locked(baudrate);
|
||||
if (err == ESP_OK && gptimer) {
|
||||
err = apply_timer_alarm_locked();
|
||||
}
|
||||
|
||||
if (resume_timer && gptimer) {
|
||||
esp_err_t restart_ret = gptimer_start(gptimer);
|
||||
if (restart_ret == ESP_OK) {
|
||||
s_timer_started = true;
|
||||
} else if (err == ESP_OK) {
|
||||
err = restart_ret;
|
||||
}
|
||||
}
|
||||
|
||||
dali_hal_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t dali_hal_get_baudrate(void)
|
||||
{
|
||||
dali_hal_lock();
|
||||
uint32_t baud = s_baudrate;
|
||||
dali_hal_unlock();
|
||||
return baud;
|
||||
}
|
||||
|
||||
size_t dali_hal_get_inited_buses(uint8_t *ids, size_t max_ids)
|
||||
{
|
||||
dali_hal_lock();
|
||||
size_t count = 0;
|
||||
for (uint8_t i = 0; i < DALI_PHY_COUNT; i++) {
|
||||
if (s_bus[i].inited) {
|
||||
if (ids && count < max_ids) {
|
||||
ids[count] = i;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
dali_hal_unlock();
|
||||
return count;
|
||||
}
|
||||
|
||||
esp_err_t dali_hal_get_bus_info(uint8_t bus_id, dali_hal_bus_info_t *info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (bus_id >= DALI_PHY_COUNT) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
dali_hal_lock();
|
||||
info->bus_id = bus_id;
|
||||
info->tx_pin = s_bus[bus_id].tx_pin;
|
||||
info->rx_pin = s_bus[bus_id].rx_pin;
|
||||
info->inited = s_bus[bus_id].inited;
|
||||
dali_hal_unlock();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // CONFIG_IDF_TARGET
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "dali_hal.h"
|
||||
|
||||
/*
|
||||
Addressing: 102.7.2.1
|
||||
0AAA AAAx - short address AAAAAA 0-63
|
||||
100G GGGx - group address GGG 0-15
|
||||
1111 1101 - broadcast unaddressed
|
||||
1111 1111 - broadcast
|
||||
x - 0 - direct arc power control (DAPC), 1 - standard command
|
||||
*/
|
||||
|
||||
|
||||
// create generic DALI message - for any bit length
|
||||
Dali_msg_t dali_msg_new_generic(uint8_t bit_length, uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3);
|
||||
|
||||
// create standard DALI message: 16,24,32 bits
|
||||
Dali_msg_t dali_msg_new(uint8_t address, uint8_t cmd1);
|
||||
Dali_msg_t dali_msg_new_3B(uint8_t address, uint8_t cmd1, uint8_t cmd2);
|
||||
Dali_msg_t dali_msg_new_4B(uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3);
|
||||
|
||||
|
||||
// short address 0-63 - 6 bits mask 0b00111111=0x3F
|
||||
// - create device address from short address
|
||||
// - device address: 0b 0AAA AAA1
|
||||
inline uint8_t dali_short_address(uint8_t address) {
|
||||
return ((address & 0x3F) << 1) | 0x01;
|
||||
}
|
||||
|
||||
// group address 0-15 - 4 bits mask 0b00001111=0x0F
|
||||
// - create group address from short address
|
||||
// - group address: 0b100G GGG1
|
||||
inline uint8_t dali_group_address(uint8_t grp) {
|
||||
return ((grp & 0x0F) << 1) | 0x81;
|
||||
}
|
||||
|
||||
void dali_send(Dali_msg_t *tx_msg);
|
||||
void dali_send_double(Dali_msg_t *dali_msg);
|
||||
int dali_query(Dali_msg_t *tx_msg, Dali_msg_t *rx_msg);
|
||||
|
||||
// DALI queries
|
||||
uint32_t dali_cmd_query_rand_addr24(uint8_t addr);
|
||||
|
||||
|
||||
void dali_find_all_short(uint8_t *dev, int with_print);
|
||||
void dali_device_info(uint8_t addr);
|
||||
void dali_device_identify(uint8_t device);
|
||||
void dali_device_reset(uint8_t device);
|
||||
|
||||
// DALI commands
|
||||
void dali_cmd(uint8_t device, uint8_t cmd);
|
||||
void dali_cmd_double(uint8_t device, uint8_t cmd);
|
||||
|
||||
void dali_cmd_off(uint8_t device);
|
||||
void dali_cmd_on(uint8_t device);
|
||||
|
||||
void dali_set_level(uint8_t device, uint8_t cmd, uint8_t level);
|
||||
void dali_group(uint8_t device1, uint8_t cmd, uint8_t group);
|
||||
|
||||
|
||||
void dali_set_dtr0(uint8_t value);
|
||||
void dali_set_dtr1(uint8_t value);
|
||||
void dali_set_dtr2(uint8_t value);
|
||||
|
||||
|
||||
void dali_cmd_initialise(uint8_t device);
|
||||
void dali_cmd_terminate();
|
||||
void dali_cmd_randomize(uint8_t device);
|
||||
void dali_cmd_withdraw(uint32_t addr24);
|
||||
|
||||
// DALI special commands
|
||||
void dali_reindex_addresses(uint8_t device);
|
||||
|
||||
void dali_set_search_addr24(uint32_t addr24);
|
||||
uint32_t dali_binary_search();
|
||||
void dali_change_short_address(int addr1, int addr2);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#define DALI_ADR_BROADCAST 0b11111111
|
||||
|
||||
#define DALI_CMD_OFF 0x00 // 102.9.7.2 11.3.2: tagetLevel=0 & OFF
|
||||
#define DALI_CMD_UP 0x01 // 102.11.3.3: for 200 ms up: tagetLevel calculated from actualLevel & fadeRate
|
||||
#define DALI_CMD_DOWN 0x02 // 102.11.3.4: for 200 ms down:
|
||||
#define DALI_CMD_STEP_UP 0x03 // 102.11.3.5: immediate one step up
|
||||
#define DALI_CMD_STEP_DOWN 0x04 // 102.11.3.6: immediate one step down
|
||||
#define DALI_CMD_RECALL_MAX 0x05 // 102.11.3.7: tagetLevel=actualLevel=maxLevel
|
||||
// INIT: moreover & output 100%, see RECALL MIN to identify light
|
||||
#define DALI_CMD_ON DALI_CMD_RECALL_MAX
|
||||
#define DALI_CMD_RECALL_MIN 0x06 // 102.11.3.7: tagetLevel=actualLevel=minLevel
|
||||
// INIT: moreover & output 0% or OFF, see RECALL MAX to identify light
|
||||
#define DALI_CMD_STEP_DOWN_OFF 0x07 //
|
||||
#define DALI_CMD_ON_STEP_UP 0x08 //
|
||||
#define DALI_CMD_DAPC_SEQ 0x09 //
|
||||
#define DALI_CMD_LAST_ACTIVE 0x0A //
|
||||
#define DALI_CMD_CONTIN_UP 0x0B //
|
||||
#define DALI_CMD_CONTIN_DOWN 0x0C //
|
||||
|
||||
#define DALI_CMD_GOTO_SCENE 0x10 // + scene number 0-15
|
||||
|
||||
#define DALI_CMD_RESET 0x20 // 102.9.11.1 11.4.2
|
||||
#define DALI_CMD_ACTUAL_IN_DTR0 0x21 //
|
||||
|
||||
#define DALI_CMD_SET_OPERATING_MODE 0x23 //
|
||||
#define DALI_CMD_IDENTIFY 0x25 // 102.9.14.3 11.4.6
|
||||
|
||||
// SCENE commands
|
||||
#define DALI_CMD_GO_TO_SCENE 0x10 //
|
||||
#define DALI_CMD_SET_SCENE 0x40 //
|
||||
#define DALI_CMD_REMOVE_FROM_SCENE 0x50 //
|
||||
|
||||
// GROUP commands
|
||||
#define DALI_CMD_ADD_TO_GROUP 0x60 //
|
||||
#define DALI_CMD_REMOVE_FROM_GROUP 0x70 //
|
||||
|
||||
// SET commands
|
||||
#define DALI_CMD_SET_MAX_LEVEL 0x2A // 102.9.6 11.4.7
|
||||
#define DALI_CMD_SET_MIN_LEVEL 0x2B // 102.9.6 11.4.8
|
||||
#define DALI_CMD_SET_SYS_FAIL_LEVEL 0x2C // 102.9.12 11.4.9
|
||||
#define DALI_CMD_SET_PW_ON_LEVEL 0x2D // 102.9.13 11.4.10
|
||||
#define DALI_CMD_SET_FADE_TIME 0x2E // 102.9.5.2 11.4.11
|
||||
#define DALI_CMD_SET_FADE_RATE 0x2F // 102.9.5.3 11.4.12
|
||||
#define DALI_CMD_SET_EXT_FADE_TIME 0x30 // 102.9.5.4 11.4.13
|
||||
|
||||
|
||||
// QUERY commands
|
||||
#define DALI_CMD_QUERY_STATUS 0x90 // 102.9.16 102.11.5.2
|
||||
#define DALI_CMD_QUERY_DTR0 0x98 // 102.11.5.11
|
||||
#define DALI_CMD_QUERY_DTR1 0x9C // 102.11.5.16
|
||||
#define DALI_CMD_QUERY_DTR2 0x9D // 102.11.5.17
|
||||
#define DALI_CMD_QUERY_OPERATING_MODE 0x9E // 102.9.9 102.11.5.18
|
||||
|
||||
#define DALI_CMD_QUERY_PHYSICAL_MIN 0x9A // 102.11.5.14
|
||||
#define DALI_CMD_QUERY_ACTUAL_LEVEL 0xA0 // 102.11.5.20
|
||||
#define DALI_CMD_QUERY_MAX_LEVEL 0xA1 // 102.11.5.21
|
||||
#define DALI_CMD_QUERY_MIN_LEVEL 0xA2 // 102.11.5.22
|
||||
#define DALI_CMD_QUERY_POWER_ON_LEVEL 0xA3 // 102.11.5.23
|
||||
#define DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL 0xA4 // 102.11.5.24
|
||||
#define DALI_CMD_QUERY_FADE_TIME 0xA5 // 102.11.5.25
|
||||
#define DALI_CMD_QUERY_EXT_FADE_TIME 0xA8 // 102.11.5.65
|
||||
|
||||
#define DALI_CMD_QUERY_SCENE_LEVEL 0xB0 //
|
||||
#define DALI_CMD_QUERY_GROUPS_0_7 0xC0 //
|
||||
#define DALI_CMD_QUERY_GROUPS_8_15 0xC1 //
|
||||
#define DALI_CMD_QUERY_RAND_ADDR_H 0xC2 // 102.11.4.31
|
||||
#define DALI_CMD_QUERY_RAND_ADDR_M 0xC3 // 102.11.4.32
|
||||
#define DALI_CMD_QUERY_RAND_ADDR_L 0xC4 // 102.11.4.33
|
||||
|
||||
|
||||
// Special commands
|
||||
#define DALI_CMD_TERMINATE 0xA1 // Releases the INITIALISE state.
|
||||
#define DALI_CMD_DTR0 0xA3 // Data Transfer Register 0
|
||||
#define DALI_CMD_INITIALISE 0xA5 // 2x Initialise 0x00=ALL 0xFF=without short address 0b0AAA AAA1=with short address
|
||||
#define DALI_CMD_RANDOMISE 0xA7 // 2x Randomises the short address of the control gear.
|
||||
#define DALI_CMD_COMPARE 0xA9 // Compares the stored short address with the received short address.
|
||||
#define DALI_CMD_WITHDRAW 0xAB // Withdraws the control gear from the system.
|
||||
#define DALI_CMD_PING 0xAD //
|
||||
#define DALI_CMD_SEARCHADDRH 0xB1 // Search Address H
|
||||
#define DALI_CMD_SEARCHADDRM 0xB3 // Search Address M
|
||||
#define DALI_CMD_SEARCHADDRL 0xB5 // Search Address L
|
||||
#define DALI_CMD_PROGRAM_SHORT 0xB7 // Program Short Address
|
||||
#define DALI_CMD_VERIFY_SHORT 0xB9 // Verify Short Address
|
||||
#define DALI_CMD_QUERY_SHORT 0xBB // Query Short Address
|
||||
#define DALI_CMD_ENABLE_DEVICE 0xC1 // Enable Device Type
|
||||
#define DALI_CMD_DTR1 0xC3 // Data Transfer Register 1
|
||||
#define DALI_CMD_DTR2 0xC5 // Data Transfer Register 2
|
||||
#define DALI_CMD_WRITE_MEM_LOC 0xC7 // Write Memory Location
|
||||
#define DALI_CMD_WRITE_MEM_NR 0xC9 // Write Memory no reply
|
||||
@@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#ifdef CONFIG_DALI_PHY_COUNT
|
||||
#define DALI_PHY_COUNT CONFIG_DALI_PHY_COUNT
|
||||
#else
|
||||
#define DALI_PHY_COUNT 16
|
||||
#endif
|
||||
#define DALI_MAX_BYTES 4
|
||||
#define DALI_MAX_BITS (DALI_MAX_BYTES * 8) // total bits
|
||||
|
||||
#ifdef CONFIG_DALI_DEFAULT_BAUDRATE
|
||||
#define DALI_DEFAULT_BAUDRATE CONFIG_DALI_DEFAULT_BAUDRATE
|
||||
#else
|
||||
#define DALI_DEFAULT_BAUDRATE 1200
|
||||
#endif
|
||||
|
||||
//
|
||||
// DALI speed: 1200 bps = 833us/bit half period = 416us
|
||||
// DALI coding 1 = low to high, 0 = high to low
|
||||
//
|
||||
|
||||
// Check if hardware is capable of multi master, very restricted timing
|
||||
// #define DALI_MULTIMASTER
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Common Timing: 101.8.1.1 tab 16
|
||||
#define DALI_TIME_HB 416 // us = HB - half bit
|
||||
#define DALI_TIME_2HB 833 // us = 2*HB - full bit or double half bit
|
||||
#define DALI_TIME_BUS_DOWN 45000 // us = 45ms = 108*HB bus power down time
|
||||
|
||||
|
||||
// we use hardware timer for timing and crystal so values are very accurate and we assume 416us pulse width
|
||||
#ifndef DALI_MULTIMASTER
|
||||
// Transmit Timing: 101.8.1.1 tab 16
|
||||
#define DALI_TX_HB_MIN 366 // us = 0.88*HB
|
||||
#define DALI_TX_HB_MAX 467 // us = 1.12*HB
|
||||
#define DALI_TX_2HB_MIN 733 // us = 0.88*2*HB
|
||||
#define DALI_TX_2HB_MAX 934 // us = 1.12*2*HB
|
||||
#else
|
||||
// Multimaster transmission: 101.8.3.1 tab 21
|
||||
#define DALI_TX_HB_MIN 400 // us = 0.96*HB
|
||||
#define DALI_TX_HB_MAX 433 // us = 1.04*HB
|
||||
#define DALI_TX_2HB_MIN 800 // us = 0.96*2*HB
|
||||
#define DALI_TX_2HB_MAX 867 // us = 1.04*2*HB
|
||||
#endif // DALI_MULTIMASTER
|
||||
|
||||
#define DALI_TX_STOP_COND 2450 // us = 2450/416 = 5.89 = 6*HB from last edge
|
||||
|
||||
// Transmit Timing: 101.8.1.2 tab 17
|
||||
// backward frame are sent independently of bus state, even if bus is busy
|
||||
#define DALI_TX_FF_BF_MIN 5500 // us = 5.5ms = 13*HB forward to backward frame time interval
|
||||
#define DALI_TX_FF_BF_MAX 10500 // us = 10.5ms = 25*HB
|
||||
|
||||
#define DALI_TX_FF_FF_MIN 13500 // us = 13.5ms = 32*HB forward to forward frame time interval
|
||||
#define DALI_TX_FF_FF_MAX 75500 // us = 75.5ms = 182*HB twice forward frame 101.9.4
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Receiving Timing:
|
||||
#ifndef DALI_MULTIMASTER
|
||||
// Receiving Timing: 101.8.2.1 tab 18 and 19
|
||||
#define DALI_RX_HB_MIN 333 // us = 0.8*HB
|
||||
#define DALI_RX_HB_MAX 500 // us = 1.2*HB
|
||||
#define DALI_RX_2HB_MIN 666 // us = 0.8*2*HB
|
||||
#define DALI_RX_2HB_MAX 1000 // us = 1.2*2*HB
|
||||
#else
|
||||
// Multimaster reception: 101.9.2.3 tab 23 and 24
|
||||
#define DALI_RX_HB_MIN 400 // us = 0.96*HB
|
||||
#define DALI_RX_HB_MAX 433 // us = 1.04*HB
|
||||
#define DALI_RX_2HB_MIN 800 // us = 0.96*2*HB
|
||||
#define DALI_RX_2HB_MAX 867 // us = 1.04*2*HB
|
||||
#endif // DALI_MULTIMASTER
|
||||
|
||||
#define DALI_RX_STOP_COND 2400 // us = 2400/416 = 5.77 = 6*HB from last edge
|
||||
|
||||
// Timing violation may inform about not identical backward frame: 101.9.6.2
|
||||
// Timing violation for start bit
|
||||
#define DALI_RX_START_TIMING_MIN 750 // us = 750/416 = 1.8 = 2*HB
|
||||
#define DALI_RX_START_TIMING_MAX 1400 // us = 1400/416 = 3.4 = 4*HB
|
||||
|
||||
// Timing violation for other bits, see break time in collision detection
|
||||
#define DALI_RX_BIT_TIMING_MIN 1200 // us = 1200/416 = 2.9 = 3*HB
|
||||
#define DALI_RX_BIT_TIMING_MAX 1400 // us = 1400/416 = 3.4 = 4*HB
|
||||
|
||||
// collision recovery: 101.9.2.4 tab 25
|
||||
#define DALI_TIME_BREAK_MIN 1200 // us = 1200/416 = 2.9 = 3*HB
|
||||
#define DALI_TIME_RECOVERY_MIN 4000 // us = 4000/416 = 9.6 = 10*HB
|
||||
|
||||
// Receiver setting times: 101.8.2.4 tab 20
|
||||
#define DALI_RX_FF_BF_MIN 2400 // us = 2400/416 = 5.77 = 6*HB forward to backward frame time interval
|
||||
#define DALI_RX_FF_BF_MAX 12400 // us = 12400/416 = 29.8 = 30*HB
|
||||
|
||||
|
||||
#define DALI_RX_BF_FF_MIN 2400 // us = 2400/416 = 5.77 = 6*HB backward to forward frame time interval
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// Collision detection:
|
||||
|
||||
// if we are transmitting we also listen to the bus in GPIO ISR, delay between TX and RX should be less then 84us
|
||||
// - value is hardware specific but in worst case it should be 500-416=84us or 416-333=83us - we use 100us
|
||||
// - multimaster is more restrictive 416-400=16us or 433-416=17us - we use 50us
|
||||
|
||||
#ifndef DALI_MULTIMASTER
|
||||
#define DALI_COLLISION_TXRX_DELTA 100 // us = 100/416 = 0.24 = 1/4*HB
|
||||
#else
|
||||
#define DALI_COLLISION_TXRX_DELTA 50 // us = 50/416 = 0.12 = 1/8*HB
|
||||
#endif // DALI_MULTIMASTER
|
||||
|
||||
typedef enum {
|
||||
DALI_BUS_UNKNOWN = 0, // after startup
|
||||
DALI_BUS_POWER_DOWN, // bus power down
|
||||
DALI_BUS_ERROR, // states <= this state are all errors
|
||||
DALI_BUS_READY, // bus powered - ready to transmit or receive
|
||||
DALI_BUS_TRANSMITTING, // bus is transmitting
|
||||
DALI_BUS_RECEIVING, // bus is receiving
|
||||
|
||||
DALI_BUS_TIME_BREAK, // bus is in time break after collision 101.9.2.4 tab 25, now is LOW=ACTIVE
|
||||
DALI_BUS_RECOVERY, // bus is in recovery after collision, wait for bus to be free, now is HIGH=IDLE
|
||||
} dali_bus_state_t;
|
||||
|
||||
typedef enum {
|
||||
TX_STATE_ERROR = 0,
|
||||
TX_STATE_COLLISION, // collision detected
|
||||
TX_STATE_IDLE, // we are not transmitting
|
||||
TX_STATE_START, // sending start bit
|
||||
TX_STATE_DATA, // sending data bits
|
||||
TX_STATE_STOP, // sending stop state
|
||||
} dali_tx_state_t;
|
||||
|
||||
typedef enum {
|
||||
RX_STATE_ERROR = 0,
|
||||
RX_STATE_IDLE, // we are not receiving
|
||||
RX_STATE_START,
|
||||
RX_STATE_DATA,
|
||||
RX_STATE_STOP,
|
||||
RX_STATE_END, // final state with received data
|
||||
} dali_rx_state_t;
|
||||
|
||||
// Frame types: 101.7.4
|
||||
typedef enum {
|
||||
DALI_MSG_FORWARD = 0, // 16,24,32 bits
|
||||
DALI_MSG_BACKWARD, // 8 bits
|
||||
DALI_MSG_RESERVED, // 20 bits - should not be used
|
||||
DALI_MSG_PROPRIETARY, // other, 8-to-15 bits may conflict with backward frames, priority only 5 101.8.3.2 tab 22
|
||||
} dali_msg_type_t;
|
||||
|
||||
// Message status
|
||||
typedef enum {
|
||||
DALI_FRAME_UNKNOWN = 0, // status not defined
|
||||
DALI_FRAME_ERROR, // error in frame
|
||||
DALI_FRAME_SEQUENCE_ERROR, // such frame should be ignored
|
||||
DALI_FRAME_COLLISION, // such frame should be ignored
|
||||
DALI_FRAME_SIZE_VIOLATION, // such frame should be ignored
|
||||
DALI_FRAME_TIME_VIOLATION, // such frame should be ignored
|
||||
DALI_FRAME_GRAY_AREA, // allow multiple interpretations - such frame should be ignored
|
||||
DALI_FRAME_OK, // frame is OK
|
||||
} dali_msg_status_t;
|
||||
|
||||
// size is 8 bytes
|
||||
struct Dali_msg {
|
||||
uint8_t id; // message ID (0-255)
|
||||
uint8_t type; // [dali_msg_type_t] type of message (forward, backward, reserved, proprietary)
|
||||
// receiving data contains time of reception from last edge change in [ms]
|
||||
uint8_t status; // [dali_msg_status_t] status of message frame (0-255)
|
||||
uint8_t length; // [1-32] length of data in bits: 8,16,24,32 bits or nonstandard eg 20 bits reserved
|
||||
uint8_t data[DALI_MAX_BYTES];
|
||||
} __attribute__((packed));
|
||||
|
||||
typedef struct Dali_msg Dali_msg_t;
|
||||
|
||||
// DEFINE in main.c and then stored in dali.c in global variables
|
||||
// #define DALI_TX_PIN ?
|
||||
// #define DALI_RX_PIN ?
|
||||
// ----------------------------------------------
|
||||
|
||||
// define HW DALI for gpio functions
|
||||
#ifndef DALI_HW_PINS
|
||||
// define HW DALI for gpio functions
|
||||
// - what we should write to pin to get HIGH/LOW
|
||||
#define DALI_TX_HIGH 1 // idle state
|
||||
#define DALI_TX_LOW 0 // active state
|
||||
// - what we should read from pin to get HIGH/LOW
|
||||
#define DALI_RX_HIGH 1 // idle state
|
||||
#define DALI_RX_LOW 0 // active state
|
||||
#endif
|
||||
|
||||
// LED onboard - debug
|
||||
#define DALI_LED_PIN 2 // output D4 = GPIO2 esp8266 and esp32
|
||||
|
||||
// onboard LED uses inverted logic - debug
|
||||
#define DALI_LED_ON 0
|
||||
#define DALI_LED_OFF 1
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
extern QueueHandle_t dali_send_queue;
|
||||
extern QueueHandle_t dali_send_reply_queue;
|
||||
extern QueueHandle_t dali_receive_queue;
|
||||
extern QueueHandle_t dali_receive_queues[DALI_PHY_COUNT];
|
||||
|
||||
extern uint8_t rx_debug_enabled; // 1 - enable debug for received messages, timing, etc
|
||||
|
||||
// preferred way: pdMS_TO_TICKS(ms) == ms * (configTICK_RATE_HZ ) / 1000
|
||||
// old way: ms / portTICK_PERIOD_MS == ms / ( 1000 / configTICK_RATE_HZ )
|
||||
inline void dali_delay_ms(TickType_t ms) { vTaskDelay(pdMS_TO_TICKS(ms)); }
|
||||
|
||||
typedef struct {
|
||||
uint8_t bus_id;
|
||||
uint8_t tx_pin;
|
||||
uint8_t rx_pin;
|
||||
bool inited;
|
||||
} dali_hal_bus_info_t;
|
||||
|
||||
esp_err_t dali_init_ports(uint8_t _dali_tx_pin, uint8_t _dali_rx_pin);
|
||||
esp_err_t dali_hal_init(uint8_t dali_id, uint8_t tx_pin, uint8_t rx_pin);
|
||||
esp_err_t dali_hal_set_baudrate(uint32_t baudrate);
|
||||
uint32_t dali_hal_get_baudrate(void);
|
||||
size_t dali_hal_get_inited_buses(uint8_t *ids, size_t max_ids);
|
||||
esp_err_t dali_hal_get_bus_info(uint8_t bus_id, dali_hal_bus_info_t *info);
|
||||
void dali_task(void *pvParameters);
|
||||
|
||||
Reference in New Issue
Block a user