This commit is contained in:
Your Name
2026-05-24 19:56:38 +03:00
parent c03cfc23fb
commit 8d5af0b70c
55 changed files with 123885 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
build/
# tmp
reference/
notes.md
tmp/
__pycache__/
logs/
# doxygen
doxygen/
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(servo2350)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
+97
View File
@@ -0,0 +1,97 @@
.. zephyr:code-sample:: blinky
:name: Blinky
:relevant-api: gpio_interface
Blink an LED forever using the GPIO API.
Overview
********
The Blinky sample blinks an LED forever using the :ref:`GPIO API <gpio_api>`.
The source code shows how to:
#. Get a pin specification from the :ref:`devicetree <dt-guide>` as a
:c:struct:`gpio_dt_spec`
#. Configure the GPIO pin as an output
#. Toggle the pin forever
See :zephyr:code-sample:`pwm-blinky` for a similar sample that uses the PWM API instead.
.. _blinky-sample-requirements:
Requirements
************
Your board must:
#. Have an LED connected via a GPIO pin (these are called "User LEDs" on many of
Zephyr's :ref:`boards`).
#. Have the LED configured using the ``led0`` devicetree alias.
Building and Running
********************
Build and flash Blinky as follows, changing ``reel_board`` for your board:
.. zephyr-app-commands::
:zephyr-app: samples/basic/blinky
:board: reel_board
:goals: build flash
:compact:
After flashing, the LED starts to blink and messages with the current LED state
are printed on the console. If a runtime error occurs, the sample exits without
printing to the console.
Build errors
************
You will see a build error at the source code line defining the ``struct
gpio_dt_spec led`` variable if you try to build Blinky for an unsupported
board.
On GCC-based toolchains, the error looks like this:
.. code-block:: none
error: '__device_dts_ord_DT_N_ALIAS_led_P_gpios_IDX_0_PH_ORD' undeclared here (not in a function)
Adding board support
********************
To add support for your board, add something like this to your devicetree:
.. code-block:: DTS
/ {
aliases {
led0 = &myled0;
};
leds {
compatible = "gpio-leds";
myled0: led_0 {
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
};
};
The above sets your board's ``led0`` alias to use pin 13 on GPIO controller
``gpio0``. The pin flags :c:macro:`GPIO_ACTIVE_LOW` mean the LED is on when
the pin is set to its low state, and off when the pin is in its high state.
Tips:
- See :dtcompatible:`gpio-leds` for more information on defining GPIO-based LEDs
in devicetree.
- If you're not sure what to do, check the devicetrees for supported boards which
use the same SoC as your target. See :ref:`get-devicetree-outputs` for details.
- See :zephyr_file:`include/zephyr/dt-bindings/gpio/gpio.h` for the flags you can use
in devicetree.
- If the LED is built in to your board hardware, the alias should be defined in
your :ref:`BOARD.dts file <devicetree-in-out-files>`. Otherwise, you can
define one in a :ref:`devicetree overlay <set-devicetree-overlays>`.
@@ -0,0 +1,51 @@
if BOARD_SERVO2350
menu "USB sample options"
depends on USB_DEVICE_STACK_NEXT
config USBD_MANUFACTURER
string "USB device sample manufacturer string"
default "Zephyr Project"
help
USB device sample manufacturer string.
config USBD_PRODUCT
string "USB device sample product string"
default "USBD sample"
help
USB device sample product stringa.
config USBD_PID
hex "USB device sample Product ID"
default 0x0001
help
USB device sample Product ID.
config USBD_SELF_POWERED
bool "USB device sample Self-powered attribute"
default y
help
Set the Self-powered attribute in the sample configuration.
config USBD_REMOTE_WAKEUP
bool "USB device sample Remote Wakeup attribute"
help
Set the Remote Wakeup attribute in the sample configuration.
config USBD_MAX_POWER
int "USB device sample bMaxPower value"
default 125
range 0 250
help
bMaxPower value in the sample configuration in 2 mA units.
config USBD_20_EXTENSION_DESC
bool "Use default USB 2.0 Extension Descriptor"
depends on USBD_BOS_SUPPORT
help
Set bcdUSB value to 0201 and use default USB 2.0 Extension Descriptor.
endmenu
endif # BOARD_SERVO2350
@@ -0,0 +1,4 @@
config BOARD_SERVO2350
select SOC_RP2350B_HAZARD3 if BOARD_SERVO2350_RP2350B_HAZARD3
select SOC_RP2350B_M33 if BOARD_SERVO2350_RP2350B_M33
@@ -0,0 +1,20 @@
board_runner_args(openocd --cmd-pre-init "source [find interface/cmsis-dap.cfg]")
if(CONFIG_ARM)
board_runner_args(openocd --cmd-pre-init "source [find target/rp2350.cfg]")
else()
board_runner_args(openocd --cmd-pre-init "source [find target/rp2350-riscv.cfg]")
endif()
board_runner_args(probe-rs "--chip=RP235x")
# The adapter speed is expected to be set by interface configuration.
# The Raspberry Pi's OpenOCD fork doesn't, so match their documentation at
# https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html#debugging-with-swd
board_runner_args(openocd --cmd-pre-init "set_adapter_speed_if_not_set 5000")
board_runner_args(uf2 "--board-id=RP2350")
include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake)
include(${ZEPHYR_BASE}/boards/common/probe-rs.board.cmake)
include(${ZEPHYR_BASE}/boards/common/uf2.board.cmake)
+6
View File
@@ -0,0 +1,6 @@
board:
name: servo2350
full_name: Servo2350
vendor: arm
socs:
- name: rp2350b
@@ -0,0 +1,75 @@
#include <zephyr/dt-bindings/pinctrl/rpi-pico-rp2350b-pinctrl.h>
&pinctrl {
uart0_default: uart0_default {
group1 {
pinmux = <UART0_TX_P0>;
};
group2 {
pinmux = <UART0_RX_P1>;
input-enable;
};
};
uart1_default: uart1_default {
// These are the same pins as SPI
group1 {
pinmux = <UART1_TX_P26>;
};
group2 {
pinmux = <UART1_RX_P27>;
input-enable;
};
};
i2c0_default: i2c0_default {
group1 {
pinmux = <I2C0_SDA_P36>, <I2C0_SCL_P37>;
input-enable;
input-schmitt-enable;
};
};
spi0_default: spi0_default {
group1 {
pinmux = <SPI0_CSN_P17>, <SPI0_SCK_P18>, <SPI0_TX_P19>;
};
group2 {
pinmux = <SPI0_RX_P16>;
input-enable;
};
};
pwm_default: pwm_default {
group1 {
pinmux = <PWM_0A_P0>,
<PWM_0B_P1>,
<PWM_1A_P2>,
<PWM_1B_P3>,
<PWM_2A_P4>,
<PWM_2B_P5>,
<PWM_3A_P6>,
<PWM_3B_P7>,
<PWM_4A_P8>,
<PWM_4B_P9>,
<PWM_5A_P10>,
<PWM_5B_P11>,
<PWM_6A_P12>,
<PWM_6B_P13>,
<PWM_7A_P14>,
<PWM_7B_P15>,
<PWM_8A_P32>,
<PWM_8B_P33>;
};
};
adc_default: adc_default {
group1 {
pinmux = <ADC_CH0_P40>, <ADC_CH1_P41>, <ADC_CH2_P42>, <ADC_CH3_P43>;
input-enable;
};
};
};
@@ -0,0 +1,406 @@
#include <freq.h>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include "servo2350-pinctrl.dtsi"
/ {
chosen {
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,console = &cdc_acm_uart0;
zephyr,shell-uart = &cdc_acm_uart0;
zephyr,code-partition = &code_partition;
};
zephyr,user {
io-channels = <&adc 0>, /* ADC channel 1 (adc2) - connected to mux */
<&adc 1>, /* ADC channel 0 (adc1) - direct channel 16 */
<&adc 2>; /* ADC channel 2 (adc3) - direct channel 17 */
};
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
label = "LED0";
};
led1: led_1 {
gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
label = "LED1";
};
};
imu_int1_pin {
compatible = "gpio-keys";
imu_int1: imu_int1 {
gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
label = "IMU_INT1";
};
};
digital_inputs {
compatible = "gpio-keys";
digital1: digital_1 {
gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_1";
};
digital2: digital_2 {
gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_2";
};
digital3: digital_3 {
gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_3";
};
digital4: digital_4 {
gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_4";
};
digital5: digital_5 {
gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_5";
};
digital6: digital_6 {
gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
label = "DIGITAL_6";
};
};
digital_outputs {
compatible = "gpio-leds";
leg0_enable: leg0_enable {
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
label = "LEG0_ENABLE";
};
leg1_enable: leg1_enable {
gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
label = "LEG1_ENABLE";
};
leg2_enable: leg2_enable {
gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
label = "LEG2_ENABLE";
};
leg3_enable: leg3_enable {
gpios = <&gpio0_hi 11 GPIO_ACTIVE_HIGH>;
label = "LEG3_ENABLE";
};
leg4_enable: leg4_enable {
gpios = <&gpio0_hi 12 GPIO_ACTIVE_HIGH>;
label = "LEG4_ENABLE";
};
leg5_enable: leg5_enable {
gpios = <&gpio0_hi 13 GPIO_ACTIVE_HIGH>;
label = "LEG5_ENABLE";
};
};
pwm_servos {
compatible = "pwm-leds";
servo1: servo_1 {
pwms = <&pwm 0 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_1";
};
servo2: servo_2 {
pwms = <&pwm 1 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_2";
};
servo3: servo_3 {
pwms = <&pwm 2 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_3";
};
servo4: servo_4 {
pwms = <&pwm 3 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_4";
};
servo5: servo_5 {
pwms = <&pwm 4 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_5";
};
servo6: servo_6 {
pwms = <&pwm 5 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_6";
};
servo7: servo_7 {
pwms = <&pwm 6 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_7";
};
servo8: servo_8 {
pwms = <&pwm 7 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_8";
};
servo9: servo_9 {
pwms = <&pwm 8 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_9";
};
servo10: servo_10 {
pwms = <&pwm 9 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_10";
};
servo11: servo_11 {
pwms = <&pwm 10 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_11";
};
servo12: servo_12 {
pwms = <&pwm 11 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_12";
};
servo13: servo_13 {
pwms = <&pwm 12 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_13";
};
servo14: servo_14 {
pwms = <&pwm 13 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_14";
};
servo15: servo_15 {
pwms = <&pwm 14 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_15";
};
servo16: servo_16 {
pwms = <&pwm 15 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_16";
};
servo17: servo_17 {
pwms = <&pwm 16 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_17";
};
servo18: servo_18 {
pwms = <&pwm 17 PWM_USEC(20000) PWM_POLARITY_NORMAL>;
label = "SERVO_18";
};
};
aliases {
watchdog0 = &wdt0;
led0 = &led0;
led1 = &led1;
imu = &icm45686;
imu-int1 = &imu_int1;
digital1 = &digital1;
digital2 = &digital2;
digital3 = &digital3;
digital4 = &digital4;
digital5 = &digital5;
digital6 = &digital6;
leg0-enable = &leg0_enable;
leg1-enable = &leg1_enable;
leg2-enable = &leg2_enable;
leg3-enable = &leg3_enable;
leg4-enable = &leg4_enable;
leg5-enable = &leg5_enable;
servo1 = &servo1;
servo2 = &servo2;
servo3 = &servo3;
servo4 = &servo4;
servo5 = &servo5;
servo6 = &servo6;
servo7 = &servo7;
servo8 = &servo8;
servo9 = &servo9;
servo10 = &servo10;
servo11 = &servo11;
servo12 = &servo12;
servo13 = &servo13;
servo14 = &servo14;
servo15 = &servo15;
servo16 = &servo16;
servo17 = &servo17;
servo18 = &servo18;
adc-mux = &adc1;
adc0 = &adc1;
adc2 = &adc2;
adc3 = &adc3;
uart0 = &uart0;
uart-bridge = &uart1;
};
pico_header: connector {
compatible = "raspberrypi,pico-header";
#gpio-cells = <2>;
gpio-map-mask = <0xffffffff 0xffffffc0>;
gpio-map-pass-thru = <0 0x3f>;
gpio-map = <0 0 &gpio0 0 0>, /* GP0 */
<1 0 &gpio0 1 0>, /* GP1 */
<2 0 &gpio0 2 0>, /* GP2 */
<3 0 &gpio0 3 0>, /* GP3 */
<4 0 &gpio0 4 0>, /* GP4 */
<5 0 &gpio0 5 0>, /* GP5 */
<6 0 &gpio0 6 0>, /* GP6 */
<7 0 &gpio0 7 0>, /* GP7 */
<8 0 &gpio0 8 0>, /* GP8 */
<9 0 &gpio0 9 0>, /* GP9 */
<10 0 &gpio0 10 0>, /* GP10 */
<11 0 &gpio0 11 0>, /* GP11 */
<12 0 &gpio0 12 0>, /* GP12 */
<13 0 &gpio0 13 0>, /* GP13 */
<14 0 &gpio0 14 0>, /* GP14 */
<15 0 &gpio0 15 0>, /* GP15 */
<16 0 &gpio0 16 0>, /* GP16 */
<17 0 &gpio0 17 0>, /* GP17 */
<18 0 &gpio0 18 0>, /* GP18 */
<19 0 &gpio0 19 0>, /* GP19 */
<20 0 &gpio0 20 0>, /* GP20 */
<21 0 &gpio0 21 0>, /* GP21 */
<22 0 &gpio0 22 0>, /* GP22 */
<26 0 &gpio0 26 0>, /* GP26 */
<27 0 &gpio0 27 0>, /* GP27 */
<28 0 &gpio0 28 0>; /* GP28 */
};
};
&flash0 {
/* W25Q16JVUXIQ_TR: 16M-bit (2MB) NOR Flash */
reg = <0x10000000 DT_SIZE_M(2)>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Code partition: 256KB for application code (starts at beginning of flash) */
code_partition: partition@0 {
label = "code";
reg = <0x00000000 DT_SIZE_K(256)>;
read-only;
};
/* Settings partition: 64KB for settings data */
settings_partition: partition@40000 {
label = "settings";
reg = <0x00040000 DT_SIZE_K(64)>;
};
/* Data partition: 1728KB for data storage (largest partition) */
data_partition: partition@50000 {
label = "data";
reg = <0x00050000 DT_SIZE_K(1728)>;
};
};
};
&uart0 {
current-speed = <115200>;
status = "okay";
pinctrl-0 = <&uart0_default>;
pinctrl-names = "default";
};
&uart1 {
current-speed = <115200>;
status = "okay";
pinctrl-0 = <&uart1_default>;
pinctrl-names = "default";
};
zephyr_udc0: &usbd {
status = "okay";
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
label = "Zephyr USB CDC-ACM";
};
};
gpio0_lo: &gpio0 {
status = "okay";
};
&gpio0_hi {
status = "okay";
};
&spi0 {
clock-frequency = <DT_FREQ_M(8)>;
pinctrl-0 = <&spi0_default>;
pinctrl-names = "default";
status = "disabled";
};
&i2c0 {
pinctrl-0 = <&i2c0_default>;
pinctrl-names = "default";
status = "okay";
clock-frequency = <I2C_BITRATE_FAST>;
icm45686: icm45686@68 {
compatible = "invensense,icm42688";
reg = <0x68>;
status = "okay";
int1-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
int2-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>;
accel-pwr-mode = <3>;
accel-ord = <9>;
accel-fs = <16>;
accel-lpf = <0>;
gyro-pwr-mode = <0>; /* turn off */
gyro-ord = <9>;
gyro-fs = <2000>;
};
};
adc0: &adc {
pinctrl-0 = <&adc_default>;
pinctrl-names = "default";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
adc1: channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
adc2: channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
adc3: channel@2 {
reg = <2>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};
&pwm {
status = "okay";
pinctrl-0 = <&pwm_default>;
pinctrl-names = "default";
divider-int-0 = <64>;
};
&timer0 {
status = "okay";
};
pico_spi: &spi0 {};
pico_i2c0: &i2c0 {};
pico_i2c1: &i2c1 {};
pico_serial: &uart0 {};
zephyr_i2c: &i2c0 {};
imu: &icm45686 {};
@@ -0,0 +1,12 @@
/dts-v1/;
/* The build system assumes that there's a cpucluster-specific file.
*
* This file provides composition of the device tree:
* 1. The common features of the SoC
* 2. Core-specific configuration.
* 3. Board-specific configuration.
*/
#include <raspberrypi/rpi_pico/rp2350b.dtsi>
#include <riscv/raspberrypi/hazard3.dtsi>
#include "servo2350.dtsi"
@@ -0,0 +1,20 @@
identifier: servo2350/rp2350b/hazard3
name: Servo2350 (Hazard3)
type: mcu
arch: riscv
flash: 16384
ram: 8192
toolchain:
- zephyr
supported:
- adc
- clock
- counter
- dma
- gpio
- hwinfo
- i2c
- pwm
- spi
- usbd
- uart
@@ -0,0 +1,10 @@
CONFIG_BUILD_OUTPUT_HEX=y
CONFIG_BUILD_OUTPUT_UF2=y
CONFIG_CLOCK_CONTROL=y
CONFIG_CONSOLE=y
CONFIG_GPIO=y
CONFIG_RESET=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_USE_DT_CODE_PARTITION=y
@@ -0,0 +1,12 @@
/dts-v1/;
/* The build system assumes that there's a cpucluster-specific file.
*
* This file provides composition of the device tree:
* 1. The common features of the SoC
* 2. Core-specific configuration.
* 3. Board-specific configuration.
*/
#include <raspberrypi/rpi_pico/rp2350b.dtsi>
#include <raspberrypi/rpi_pico/m33.dtsi>
#include "servo2350.dtsi"
@@ -0,0 +1,21 @@
identifier: servo2350/rp2350b/m33
name: Servo2350 (Cortex-M33)
type: mcu
arch: arm
flash: 16384
ram: 8192
toolchain:
- zephyr
- gnuarmemb
supported:
- adc
- clock
- counter
- dma
- gpio
- hwinfo
- i2c
- pwm
- spi
- usbd
- uart
@@ -0,0 +1,10 @@
CONFIG_BUILD_OUTPUT_HEX=y
CONFIG_BUILD_OUTPUT_UF2=y
CONFIG_CLOCK_CONTROL=y
CONFIG_CONSOLE=y
CONFIG_GPIO=y
CONFIG_RESET=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_USE_DT_CODE_PARTITION=y
@@ -0,0 +1,8 @@
# Checking and set 'adapter speed'.
# Set the adaptor speed, if unset, and given as an argument.
proc set_adapter_speed_if_not_set { speed } {
puts "checking adapter speed..."
if { [catch {adapter speed} ret] } {
adapter speed $speed
}
}
+2
View File
@@ -0,0 +1,2 @@
add_subdirectory(multiplexer)
+5
View File
@@ -0,0 +1,5 @@
menu "Servo2350 Device Drivers"
rsource "multiplexer/Kconfig"
endmenu
@@ -0,0 +1 @@
add_subdirectory_ifdef(CONFIG_CD74HC4067 cd74hc4067)
+5
View File
@@ -0,0 +1,5 @@
menu "Multiplexer Drivers"
rsource "cd74hc4067/Kconfig"
endmenu
@@ -0,0 +1,5 @@
if(CONFIG_CD74HC4067)
target_sources(app PRIVATE ${CMAKE_CURRENT_LIST_DIR}/cd74hc4067.c)
target_include_directories(app PRIVATE ${CMAKE_CURRENT_LIST_DIR})
endif()
@@ -0,0 +1,23 @@
menuconfig CD74HC4067
bool "TI CD74HC4067 analog multiplexer"
depends on GPIO
help
Enable support for the TI CD74HC4067 16-channel analog/digital
multiplexer/demultiplexer controlled through GPIO selection pins.
if CD74HC4067
config CD74HC4067_INIT_PRIORITY
int "Initialization priority"
default 80
help
Device initialization priority for the CD74HC4067 driver. This should
be higher (lower numeric value) than any clients that depend on the
multiplexer being ready.
module = CD74HC4067
module-str = cd74hc4067
source "subsys/logging/Kconfig.template.log_config"
endif # CD74HC4067
@@ -0,0 +1,217 @@
#define DT_DRV_COMPAT cd74hc4067
#include <errno.h>
#include <stdbool.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include "cd74hc4067.h"
LOG_MODULE_REGISTER(cd74hc4067, CONFIG_CD74HC4067_LOG_LEVEL);
#define CD74HC4067_CHANNEL_COUNT 16U
#define CD74HC4067_SELECTION_PIN_COUNT 4U
struct cd74hc4067_config {
struct gpio_dt_spec sel[CD74HC4067_SELECTION_PIN_COUNT];
struct gpio_dt_spec enable;
bool has_enable;
};
struct cd74hc4067_data {
struct k_mutex lock;
uint8_t current_channel;
bool enabled;
};
static int cd74hc4067_sync_enable_state(const struct device *dev, bool enable)
{
const struct cd74hc4067_config *cfg = dev->config;
struct cd74hc4067_data *data = dev->data;
int ret = 0;
if (!cfg->has_enable) {
if (!enable) {
return -ENOTSUP;
}
data->enabled = true;
return 0;
}
if (!device_is_ready(cfg->enable.port)) {
LOG_ERR("%s enable GPIO not ready", dev->name);
return -ENODEV;
}
ret = gpio_pin_set_dt(&cfg->enable, enable ? 0 : 1);
if (ret == 0) {
data->enabled = enable;
}
return ret;
}
static int cd74hc4067_configure_gpios(const struct device *dev)
{
const struct cd74hc4067_config *cfg = dev->config;
int ret;
for (size_t i = 0; i < ARRAY_SIZE(cfg->sel); i++) {
const struct gpio_dt_spec *sel = &cfg->sel[i];
if (!device_is_ready(sel->port)) {
LOG_ERR("%s sel%zu GPIO not ready", dev->name, i);
return -ENODEV;
}
ret = gpio_pin_configure_dt(sel, GPIO_OUTPUT_INACTIVE);
if (ret != 0) {
LOG_ERR("%s failed to configure sel%zu GPIO (%d)", dev->name, i, ret);
return ret;
}
}
if (cfg->has_enable) {
if (!device_is_ready(cfg->enable.port)) {
LOG_ERR("%s enable GPIO not ready", dev->name);
return -ENODEV;
}
ret = gpio_pin_configure_dt(&cfg->enable, GPIO_OUTPUT_HIGH);
if (ret != 0) {
LOG_ERR("%s failed to configure enable GPIO (%d)", dev->name, ret);
return ret;
}
}
return 0;
}
static int cd74hc4067_init(const struct device *dev)
{
const struct cd74hc4067_config *cfg = dev->config;
struct cd74hc4067_data *data = dev->data;
int ret;
k_mutex_init(&data->lock);
data->current_channel = 0U;
data->enabled = !cfg->has_enable;
ret = cd74hc4067_configure_gpios(dev);
if (ret != 0) {
return ret;
}
/* Latch the default channel (0) */
ret = cd74hc4067_select_channel(dev, 0U);
if (ret != 0) {
return ret;
}
LOG_INF("%s initialized (enable pin %s)", dev->name, cfg->has_enable ? "present" : "absent");
return 0;
}
int cd74hc4067_select_channel(const struct device *dev, uint8_t channel)
{
const struct cd74hc4067_config *cfg = dev->config;
struct cd74hc4067_data *data = dev->data;
int ret = 0;
if (channel >= CD74HC4067_CHANNEL_COUNT) {
return -EINVAL;
}
ret = k_mutex_lock(&data->lock, K_FOREVER);
if (ret != 0) {
return ret;
}
for (size_t i = 0; i < ARRAY_SIZE(cfg->sel); i++) {
ret = gpio_pin_set_dt(&cfg->sel[i], (channel >> i) & 0x1);
if (ret != 0) {
LOG_ERR("%s failed to drive sel%zu GPIO (%d)", dev->name, i, ret);
goto out;
}
}
data->current_channel = channel;
out:
k_mutex_unlock(&data->lock);
return ret;
}
int cd74hc4067_enable(const struct device *dev)
{
struct cd74hc4067_data *data = dev->data;
int ret;
ret = k_mutex_lock(&data->lock, K_FOREVER);
if (ret != 0) {
return ret;
}
ret = cd74hc4067_sync_enable_state(dev, true);
k_mutex_unlock(&data->lock);
return ret;
}
int cd74hc4067_disable(const struct device *dev)
{
struct cd74hc4067_data *data = dev->data;
int ret;
ret = k_mutex_lock(&data->lock, K_FOREVER);
if (ret != 0) {
return ret;
}
ret = cd74hc4067_sync_enable_state(dev, false);
k_mutex_unlock(&data->lock);
return ret;
}
uint8_t cd74hc4067_get_current_channel(const struct device *dev)
{
struct cd74hc4067_data *data = dev->data;
uint8_t channel;
k_mutex_lock(&data->lock, K_FOREVER);
channel = data->current_channel;
k_mutex_unlock(&data->lock);
return channel;
}
#define CD74HC4067_DEFINE(inst) \
BUILD_ASSERT(DT_PROP_LEN(DT_DRV_INST(inst), sel_gpios) == CD74HC4067_SELECTION_PIN_COUNT, \
"cd74hc4067 requires exactly 4 selection GPIOs"); \
static const struct cd74hc4067_config cd74hc4067_config_##inst = { \
.sel = { \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), sel_gpios, 0), \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), sel_gpios, 1), \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), sel_gpios, 2), \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), sel_gpios, 3), \
}, \
.enable = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, enable_gpios), \
(GPIO_DT_SPEC_GET(DT_DRV_INST(inst), enable_gpios)), \
((struct gpio_dt_spec){0})), \
.has_enable = DT_INST_NODE_HAS_PROP(inst, enable_gpios), \
}; \
static struct cd74hc4067_data cd74hc4067_data_##inst; \
DEVICE_DT_INST_DEFINE(inst, cd74hc4067_init, NULL, &cd74hc4067_data_##inst, \
&cd74hc4067_config_##inst, POST_KERNEL, \
CONFIG_CD74HC4067_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(CD74HC4067_DEFINE)
@@ -0,0 +1,64 @@
#ifndef SERVO2350_DRIVERS_MULTIPLEXER_CD74HC4067_H_
#define SERVO2350_DRIVERS_MULTIPLEXER_CD74HC4067_H_
#include <stdint.h>
#include <zephyr/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Select one of the 16 multiplexed channels.
*
* @param dev CD74HC4067 device instance.
* @param channel Channel number to route (0-15).
*
* @retval 0 If the channel was selected.
* @retval -EINVAL If the channel number is out of range.
* @retval -ENODEV If one of the selection GPIOs is not ready.
* @retval other Negative errno value from the GPIO driver.
*/
int cd74hc4067_select_channel(const struct device *dev, uint8_t channel);
/**
* @brief Enable the multiplexer output (drives the /EN pin low).
*
* If the instance does not provide an enable GPIO, this call is a no-op.
*
* @param dev CD74HC4067 device instance.
*
* @retval 0 On success.
* @retval -ENODEV If the enable GPIO is not ready.
* @retval negative errno on GPIO failures.
*/
int cd74hc4067_enable(const struct device *dev);
/**
* @brief Disable the multiplexer output (drives the /EN pin high).
*
* @param dev CD74HC4067 device instance.
*
* @retval 0 On success.
* @retval -ENOTSUP If the instance does not expose an enable GPIO.
* @retval -ENODEV If the enable GPIO is not ready.
* @retval negative errno on GPIO failures.
*/
int cd74hc4067_disable(const struct device *dev);
/**
* @brief Return the last channel value written to the device.
*
* @param dev CD74HC4067 device instance.
*
* @return Channel number in the range [0, 15].
*/
uint8_t cd74hc4067_get_current_channel(const struct device *dev);
#ifdef __cplusplus
}
#endif
#endif /* SERVO2350_DRIVERS_MULTIPLEXER_CD74HC4067_H_ */
+27
View File
@@ -0,0 +1,27 @@
description: |
GPIO-controlled 16-channel analog/digital multiplexer/demultiplexer.
The CD74HC4067 has 4 selection pins (S0-S3) that select one of 16 channels.
Supports optional common enable pin for power management.
compatible: "cd74hc4067"
include: base.yaml
properties:
sel-gpios:
type: phandle-array
required: true
description: |
GPIO pins for channel selection (S0-S3).
Array must contain exactly 4 GPIOs:
- Index 0: S0 (LSB)
- Index 1: S1
- Index 2: S2
- Index 3: S3 (MSB)
enable-gpios:
type: phandle-array
description: |
Optional GPIO for common enable pin (E).
When low, enables the multiplexer. When high, disables it.
+31
View File
@@ -0,0 +1,31 @@
# Basic
CONFIG_GPIO=y
CONFIG_PWM=y
# Serial
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_UART_LINE_CTRL=y
# USB
CONFIG_USB_DEVICE_STACK_NEXT=y
CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=y
CONFIG_CDC_ACM_SERIAL_PRODUCT_STRING="Servo2350"
CONFIG_CDC_ACM_SERIAL_PID=0x0004
# Route console/logs to USB CDC ACM (configured via device tree)
CONFIG_LOG=n
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_UART_CONSOLE=y
CONFIG_LOG_BACKEND_UART=y
# Shell
CONFIG_SHELL=y
CONFIG_USBD_SHELL=y
CONFIG_SHELL_HISTORY=y
# ZBUS Configuration
CONFIG_ZBUS=y
CONFIG_ZBUS_RUNTIME_OBSERVERS=y
+12
View File
@@ -0,0 +1,12 @@
sample:
name: Blinky Sample
tests:
sample.basic.blinky:
tags:
- LED
- gpio
filter: dt_enabled_alias_with_parent_compat("led0", "gpio-leds")
depends_on: gpio
harness: led
integration_platforms:
- frdm_k64f
+103
View File
@@ -0,0 +1,103 @@
#include "led.h"
#include "zbus_message.h"
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/zbus/zbus.h>
LOG_MODULE_REGISTER(led, LOG_LEVEL_INF);
#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
static const struct gpio_dt_spec led0_gpio = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led1_gpio = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
static bool led0_state = false;
static bool led1_state = false;
/* ZBUS handler - update LED state */
static void leds_zbus_handler(const struct zbus_channel *chan)
{
const struct leds_message *msg;
int ret;
const struct gpio_dt_spec *target_gpio;
bool *target_state;
msg = zbus_chan_const_msg(chan);
if (msg == NULL) {
return;
}
if (msg->led_id == 0) {
target_gpio = &led0_gpio;
target_state = &led0_state;
} else if (msg->led_id == 1) {
target_gpio = &led1_gpio;
target_state = &led1_state;
} else {
return;
}
*target_state = msg->led_value;
ret = gpio_pin_set_dt(target_gpio, msg->led_value ? 1 : 0);
if (ret != 0) {
LOG_ERR("Failed to set LED%d: %d", msg->led_id, ret);
return;
}
LOG_DBG("LED%d updated: %s (LED0=%s, LED1=%s)",
msg->led_id, msg->led_value ? "ON" : "OFF",
led0_state ? "ON" : "OFF", led1_state ? "ON" : "OFF");
}
ZBUS_LISTENER_DEFINE(leds_listener, leds_zbus_handler);
int led_init(void)
{
int ret;
if (!device_is_ready(led0_gpio.port)) {
LOG_ERR("LED0 GPIO device not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&led0_gpio, GPIO_OUTPUT_INACTIVE);
if (ret != 0) {
LOG_ERR("Failed to configure LED0 GPIO: %d", ret);
return ret;
}
if (!device_is_ready(led1_gpio.port)) {
LOG_ERR("LED1 GPIO device not ready");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&led1_gpio, GPIO_OUTPUT_INACTIVE);
if (ret != 0) {
LOG_ERR("Failed to configure LED1 GPIO: %d", ret);
return ret;
}
ret = zbus_chan_add_obs(&leds_chan, &leds_listener, K_MSEC(200));
if (ret != 0) {
LOG_ERR("Failed to add LED listener: %d", ret);
return ret;
}
ret = gpio_pin_set_dt(&led0_gpio, 0);
ret |= gpio_pin_set_dt(&led1_gpio, 0);
if (ret != 0) {
LOG_ERR("Failed to initialize LEDs");
return ret;
}
LOG_INF("LED driver initialized (LED0: GPIO %d, LED1: GPIO %d)",
led0_gpio.pin, led1_gpio.pin);
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef LED_H
#define LED_H
/**
* @file led.h
* @brief LED control via GPIO and ZBUS
*/
/**
* @brief Initialize LED driver
*
* @return 0 on success, negative errno on failure
*/
int led_init(void);
#endif /* LED_H */
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
int main(void)
{
int ret;
/* LED driver - status indicators */
ret = led_init();
if (ret != 0) {
LOG_ERR("Failed to initialize LED driver: %d", ret);
}
while (1) {
k_msleep(1000);
}
return 0;
}
+127
View File
@@ -0,0 +1,127 @@
#include "zbus_message.h"
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <zephyr/zbus/zbus.h>
LOG_MODULE_REGISTER(led_shell, LOG_LEVEL_INF);
/**
* @brief Parse and publish an LED command to leds_chan.
*
* Usage:
* led set <id> <on|off>
*
* @param shell Shell instance
* @param argc Argument count (must be 3: "set", id, state)
* @param argv Argument vector
* @return 0 on success, negative errno on failure
*/
static int cmd_led_set(const struct shell *shell, size_t argc, char **argv)
{
struct leds_message msg;
int ret;
/* Parse LED id */
char *endptr;
long id = strtol(argv[1], &endptr, 10);
if (*endptr != '\0' || id < 0 || id > 1) {
shell_error(shell, "Invalid LED id '%s'. Use 0 or 1.", argv[1]);
return -EINVAL;
}
/* Parse state */
if (strcmp(argv[2], "on") == 0) {
msg.led_value = true;
} else if (strcmp(argv[2], "off") == 0) {
msg.led_value = false;
} else {
shell_error(shell, "Invalid state '%s'. Use 'on' or 'off'.", argv[2]);
return -EINVAL;
}
msg.led_id = (uint8_t)id;
ret = zbus_chan_pub(&leds_chan, &msg, K_MSEC(200));
if (ret != 0) {
shell_error(shell, "Failed to publish LED command: %d", ret);
return ret;
}
shell_print(shell, "LED%d turned %s", msg.led_id, msg.led_value ? "ON" : "OFF");
return 0;
}
/**
* @brief Turn on all LEDs.
*/
static int cmd_led_all_on(const struct shell *shell, size_t argc, char **argv)
{
struct leds_message msg;
int ret;
for (uint8_t i = 0; i <= 1; i++) {
msg.led_id = i;
msg.led_value = true;
ret = zbus_chan_pub(&leds_chan, &msg, K_MSEC(200));
if (ret != 0) {
shell_error(shell, "Failed to turn on LED%d: %d", i, ret);
return ret;
}
}
shell_print(shell, "All LEDs turned ON");
return 0;
}
/**
* @brief Turn off all LEDs.
*/
static int cmd_led_all_off(const struct shell *shell, size_t argc, char **argv)
{
struct leds_message msg;
int ret;
for (uint8_t i = 0; i <= 1; i++) {
msg.led_id = i;
msg.led_value = false;
ret = zbus_chan_pub(&leds_chan, &msg, K_MSEC(200));
if (ret != 0) {
shell_error(shell, "Failed to turn off LED%d: %d", i, ret);
return ret;
}
}
shell_print(shell, "All LEDs turned OFF");
return 0;
}
/* Subcommands for the "led" root command */
SHELL_STATIC_SUBCMD_SET_CREATE(sub_led,
SHELL_CMD_ARG(set, NULL,
"Set a single LED state.\n"
"Usage: led set <id> <on|off>\n"
" id : 0 or 1\n"
" state: on | off",
cmd_led_set, 3, 0),
SHELL_CMD(allon, NULL,
"Turn all LEDs on.\n"
"Usage: led allon",
cmd_led_all_on),
SHELL_CMD(alloff, NULL,
"Turn all LEDs off.\n"
"Usage: led alloff",
cmd_led_all_off),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_REGISTER(led, &sub_led,
"LED control commands.\n"
" led set <id> <on|off> - Set LED state\n"
" led allon - Turn all LEDs on\n"
" led alloff - Turn all LEDs off",
NULL);
+16
View File
@@ -0,0 +1,16 @@
#include "zbus_message.h"
#include <zephyr/zbus/zbus.h>
/**
* @brief ZBUS channel definition for LED control.
*
* No validator is used; any leds_message value is accepted.
* Observers are registered at runtime via zbus_chan_add_obs().
*/
ZBUS_CHAN_DEFINE(leds_chan, /* channel name */
struct leds_message, /* message type */
NULL, /* validator */
NULL, /* user data */
ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(.led_id = 0, .led_value = false));
+30
View File
@@ -0,0 +1,30 @@
#ifndef ZBUS_MESSAGE_H
#define ZBUS_MESSAGE_H
/**
* @file zbus_message.h
* @brief ZBUS channel and message definitions for LED control
*/
#include <stdbool.h>
#include <zephyr/zbus/zbus.h>
/**
* @brief Message published to leds_chan to control an LED.
*
* @param led_id LED index (0 or 1)
* @param led_value true = ON, false = OFF
*/
struct leds_message {
uint8_t led_id;
bool led_value;
};
/**
* @brief ZBUS channel for LED control messages.
*
* Publish a struct leds_message to this channel to set an LED state.
*/
ZBUS_CHAN_DECLARE(leds_chan);
#endif /* ZBUS_MESSAGE_H */