-
Notifications
You must be signed in to change notification settings - Fork 7.3k
drivers: Add DALI drivers to Zephyr #88128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
drivers: Add DALI drivers to Zephyr #88128
Conversation
521bf87
to
55220d2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to see a DALI PR! I skimmed the PR and have a few initial comments.
drivers/dali/Kconfig
Outdated
help | ||
How many frames should be buffered in the recv queue | ||
|
||
rsource "Kconfig.lpc11u6x" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
source should suffice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to source
.
drivers/dali/include/lpc11u6x.h
Outdated
/* 4.4 Register description*/ | ||
/* clang-format off */ | ||
typedef struct { /*!< (@ 0x40048000) SYSCON Structure */ | ||
__IO uint32_t SYSMEMREMAP; /*!< (@ 0x40048000) System memory remap */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use snake_case for variable names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of work in progress. I opened another PR, so that I can use clock_control
for the syscon
stuff. For the rest I would like to leave this conversation open, as a reminder of work to be done.
drivers/dali/include/lpc11u6x.h
Outdated
#define LPC_PINT_BASE 0xA0004000UL | ||
|
||
/* 4.4 Register description*/ | ||
/* clang-format off */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why turn it off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the clang-format off
.
drivers/dali/include/lpc11u6x.h
Outdated
#define LPC_GPIO_PORT ((LPC_GPIO_PORT_Type *)LPC_GPIO_PORT_BASE) | ||
|
||
/* 18.6.1 configuration register */ | ||
#define SCT_CONFIG_UNIFY (1U << 0U) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using bit macro
samples/drivers/dali/src/main.c
Outdated
|
||
/* get the DALI device */ | ||
const struct device *dali_dev = DEVICE_DT_GET(DALI_NODE); | ||
if (!dali_dev) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use device_is_ready. It also checks if device is null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use devcie_is_ready
now.
5d932a6
to
43613e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments, but besides that as it introduces a new API, I think this should be presented at the Architecture working group.
CC @carlescufi @henrikbrixandersen
b75830a
to
c551005
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description of this PR is quite elaborate, maybe this could be used as a documentation starting point too?
Next to that, it would be nice to have some tests in place, and shell commands.
66f7806
to
cd16b54
Compare
992075c
to
20373ec
Compare
7334e5b
to
607233c
Compare
@SvenHaedrich would you be available to present this topic at the Architecture WG meeting in the near future? |
Sure, I will be happy to present my PR to the Working-Group. You can reach out to me at [email protected] |
bb8ec94
to
c9e8506
Compare
@SvenHaedrich I see you squashed everything into a single commit, it think it would be better to split these into some smaller chunks:
|
faadf6b
to
f12628e
Compare
c54a16a
to
4514d74
Compare
description: DALI interface for nxp lpc11u6x | ||
|
||
compatible: "nxp,dali-lpc11u6x" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is very odd that there would need to be a binding aiming at just one single SoC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. It is just that I have this mature driver for this specific chip. The driver is tailored around the counter peripherals of that chip. I guess there are more NXP cores using these peripherals but I never investigated this. As of now there are no Zephyr drivers for these counters. So, the driver treats the counters as "DALI peripherals" and the abstraction is the DALI API.
I don't want to be the one that decides whether this driver implementation is too specific to be part of Zephyr.
07c9a2c
to
1447272
Compare
Add a DALI driver to Zephyr
DALI is the digital addressable lighting interface, a standard for professional lighting solutions. In its core the standard is based on Manchester encoded exchange of frames. Because specific behavior for the interface is required by the DALI-Alliance a dedicated driver is needed to pass the mandatory tests.
Driver Status
Here are two implementations for a DALI driver.
lpcxpresso11u68
This driver is very specific for this chip. There was no counter or pwm implementation available at the time of the implementation. So, the driver accesess these peripherals directly. This makes this implementation hardly transferable to other platforms. The reason to include this driver in this PR is the fact that it passes all the relevant DALI tests. Hence,m it can serve as a source of inspiration how to achieve full compatibility. You can select this implementation with
export BOARD=lpcxpresso11u68
nucleo_f091rc
This is a more generic implementation of a DALI driver. Though, it requires a PWM peripheral that can capture edge events on gpio pins. This is true for a variety of STM32 controllers. Though, this driver is only tested for the f091.
export BOARD=nucleo_f091rc
Sample program
This code includes a sample application that sends DALI frames. These result in a blink action on LEDs (or device type 6 control gears, as we call it in the business) that are connected to the DALI bus. The device tree overlays are optimized for a DALI 2 click adapter providing the physical interface to the DALI bus. Refer to the devicetree files to find details about the connection of Rx and Tx lines.
You can build the sample code:
west build --board $BOARD samples/drivers/dali
LPC11U6x Dali Test Status
This low-level driver passes the following tests from the DALI-Alliance. (DiiA V2 2.6.0.0 - February 2024) This list will
not track tests that relate to the hardware of the DALI interface.
STM PWM Dali Test Status
This low-level driver passes the following tests from the DALI-Alliance. (DiiA V2 2.6.0.0 - February 2024) This list will
not track tests that relate to the hardware of the DALI interface.
Design Considerations for DALI Low Level Driver
API-Design
The API for the DALI bus needs to provide the following functionalities:
Further requirements
Frame Types
Currently, supported events and frame types are:
Receive
dev
is a pointer to a DALI devicerx_frame
is a buffer for a DALI frame or an eventtimeout
timeout perioddali_receive
will return the next frame received from the DALI bus. The function has an input queue, to ensure that no frame or event is lost.return codes
*rx_frame
*rx_frame
is invalidSend
dev
is a pointer to a DALI devicetx_frame
is a buffer holding a single DALI frame, the buffer is copied into data structures of the low-level driver and may be discarded after returnThis function supports async operation. Any frame is stored into an internal send slot and the
dali_send
returns immediately.dali_send
maintains two send slots. One slot is reserved for backward frames. The other slot is used for all kind of forward frames. In case of a forward frame in its slot that is pending for transmission, it is still possible to provide a backward frame. That backward frame will be transmitted before the pending forward frame, whenever possible. There is a strict timing limit from the DALI standard (see IEC 62386-101:2022 8.1.2 Table 17) for the timing of backward frames. When these restrictions can not be fulfilled, the backward frame may be dropped and an error code returned.return codes
Abort
dev
is a pointer to a DALI devicedali_abort
will abort all pending or ongoing forward frame transmissions. Transmission will be aborted, regardless of bit timings, at the shortest possible time. This can result in corrupt a frame.Reasoning
Firstly, I think an async send is the way to go. I can not think of a scenario where it is favorable to block execution until the frame is sent. Arguments for that:
Secondly, on using a send queue. I feel my stomach ache about that. But as good practitioners we need to grow above feelings. Here is some reasoning. To build a sound API for DALI we need to consider what we require from the higher stack levels.
There a basically two kind of DALI controls:
Control Gears
Let's start with the case 2 as it's much easier. All messages follow a simple sequence.
Actually, just a best effort to transmit the backward frame is required from the low-level-driver. The backward frame is send regardless of collisions, and I would say even a bus down condition does not matter as the transmission of the backward frame is bound to strict timing limits.
The higher levels of the DALI protocol ensure that there is only a single frame processed at a time. Hence, it is sufficient to have a single input slot for forward and backward frames.
Optionally, the low-level-driver can track whether the timing requirements for a backframe are meet. In case the backframe is overdue, the low-level-driver can decide to drop the backframe completely as it will be disregarded anyhow.
Control Devices
Now look at e.g. control devices with an application controller. Firstly, we have exactly the sequence pattern from above, where forward frames are received and require processing and an optional response from the DALI-stack. Parallel to that there might be an event that requires signalling (or it triggers sending of a forward frame typically something like a DAPC command).
The processing of a forward frame needs to have a higher priority than sending an event as it might require a backward frame that obeys the strict timing requirements. Actually, an event has to wait until the bus is idle for the time defined by the event´s inter frame timing priority.
How should this be expressed in code? The main loop of the DALI stack will be identical to the one for control gears.
Ideally events are generated in a different context and
dali_send
will keep the event frame in a buffer and wait for the specified inter frame timing while back frames might sneak by.Collisions
The concepts of collision avoidance, detection, and recovery is found in IEC 62386-101:2022 9.2. These concepts apply for forward frames only. Collision of backward frames are neither detected, nor is it necessary or possible to re-send these. When the low-level driver detects a collision it will destroy the ongoing frame transmission by application of a break signal (see IEC 62386-101:2022 9.2.4 Table 25). After the recovery time the low-level-driver will automatically re-try to send the frame. It is the task of the application layer to control and tame the re-sending if necessary.
So, sending a forward frame from the application layer needs to look like this:
Transactions
The concept of transactions is found in IEC 62386-101:2022 9.3: The purpose of transactions is to ensure that a sequence of commands send by one control device cannot be interrupted by another control device.
Look, for instance, at the retrieval of a memory bank cell for a control gear. The required command sequence is something like:
There are two commands that prepare the query, and a query command that expects backward frames
from the addressed gears. The code to transmit these frames can look like this:
Obviously, there are a lot of code repetitions which can be saved when defining a function
Probably, this will be a blocking function. This should not hurt too much, as we most likely wait for a backframe anyhow.
We have to wait until the transmission of the preceding frame has finished before we call for the next frame to be send to the DALI bus. Otherwise, we can expect to see a
EBUSY
error code.