From 6ba56cbbeaef468863815774115b23445157a0f2 Mon Sep 17 00:00:00 2001 From: Aleksas Pielikis Date: Fri, 28 Apr 2023 13:49:54 +0300 Subject: [PATCH] Add Odroid C4 project --- .gitignore | 2 + .vscode/settings.json | 22 + example/driver_ws2812b_basic.c | 4 +- interface/driver_ws2812b_interface.h | 14 +- interface/driver_ws2812b_interface_template.c | 24 +- project/odroidc4/CMakeLists.txt | 212 ++++++++ project/odroidc4/J8Header.jpg | Bin 0 -> 89332 bytes project/odroidc4/Makefile | 149 +++++ project/odroidc4/README.md | 184 +++++++ project/odroidc4/cmake/VERSION | 1 + project/odroidc4/cmake/config.cmake.in | 44 ++ project/odroidc4/cmake/uninstall.cmake | 58 ++ .../src/odroidc4_driver_ws2812b_interface.c | 134 +++++ project/odroidc4/interface/inc/spi.h | 202 +++++++ project/odroidc4/interface/src/spi.c | 514 ++++++++++++++++++ project/odroidc4/src/main.c | 344 ++++++++++++ project/raspberrypi4b/CMakeLists.txt | 8 +- .../raspberrypi4b_driver_ws2812b_interface.c | 20 +- .../src/stm32f407_driver_ws2812b_interface.c | 20 +- src/driver_ws2812b.c | 28 +- src/driver_ws2812b.h | 28 +- test/driver_ws2812b_write_test.c | 33 +- 22 files changed, 2009 insertions(+), 36 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 project/odroidc4/CMakeLists.txt create mode 100644 project/odroidc4/J8Header.jpg create mode 100644 project/odroidc4/Makefile create mode 100644 project/odroidc4/README.md create mode 100644 project/odroidc4/cmake/VERSION create mode 100644 project/odroidc4/cmake/config.cmake.in create mode 100644 project/odroidc4/cmake/uninstall.cmake create mode 100644 project/odroidc4/driver/src/odroidc4_driver_ws2812b_interface.c create mode 100644 project/odroidc4/interface/inc/spi.h create mode 100644 project/odroidc4/interface/src/spi.c create mode 100644 project/odroidc4/src/main.c diff --git a/.gitignore b/.gitignore index c6127b3..4d3ceeb 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ modules.order Module.symvers Mkfile.old dkms.conf + +build \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0066b80 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "cmake.sourceDirectory": "/root/ws2812b/project/odroidc4", + "cmake.debugConfig": { + "cwd": "${workspaceFolder}", + "args": [ + "-t", "write", + "--number", "24", + "--times", "100", + // "--color", "0", + "--color", "16711680", // red + // "--color", "65280", // green + // "--color", "255", // blue + ], + }, + "files.associations": { + "driver_ws2812b_basic.h": "c", + "spidev.h": "c", + "spi.h": "c", + "driver_ws2812b_interface.h": "c", + "stdlib.h": "c" + } +} \ No newline at end of file diff --git a/example/driver_ws2812b_basic.c b/example/driver_ws2812b_basic.c index 6fffcd4..7226cba 100644 --- a/example/driver_ws2812b_basic.c +++ b/example/driver_ws2812b_basic.c @@ -51,9 +51,11 @@ uint8_t ws2812b_basic_init(void) /* link interface function */ DRIVER_WS2812B_LINK_INIT(&gs_handle, ws2812b_handle_t); - DRIVER_WS2812B_LINK_SPI_10MHZ_INIT(&gs_handle, ws2812b_interface_spi_10mhz_init); + DRIVER_WS2812B_LINK_SPI_INIT(&gs_handle, ws2812b_interface_spi_init); DRIVER_WS2812B_LINK_SPI_DEINIT(&gs_handle, ws2812b_interface_spi_deinit); DRIVER_WS2812B_LINK_SPI_WRITE_COMMAND(&gs_handle, ws2812b_interface_spi_write_cmd); + DRIVER_WS2812B_LINK_ONE_CODE(&gs_handle, ws2812b_interface_one_code); + DRIVER_WS2812B_LINK_ZERO_CODE(&gs_handle, ws2812b_interface_zero_code); DRIVER_WS2812B_LINK_DELAY_MS(&gs_handle, ws2812b_interface_delay_ms); DRIVER_WS2812B_LINK_DEBUG_PRINT(&gs_handle, ws2812b_interface_debug_print); diff --git a/interface/driver_ws2812b_interface.h b/interface/driver_ws2812b_interface.h index 4a2a2ff..8800702 100644 --- a/interface/driver_ws2812b_interface.h +++ b/interface/driver_ws2812b_interface.h @@ -57,7 +57,7 @@ extern "C"{ * - 1 spi init 10mhz failed * @note none */ -uint8_t ws2812b_interface_spi_10mhz_init(void); +uint8_t ws2812b_interface_spi_init(void); /** * @brief interface spi bus deinit @@ -68,6 +68,18 @@ uint8_t ws2812b_interface_spi_10mhz_init(void); */ uint8_t ws2812b_interface_spi_deinit(void); +/** + * @brief WS1812B One Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_one_code(void); + +/** + * @brief WS1812B Zero Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_zero_code(void); + /** * @brief interface spi bus write command * @param[in] *buf points to a data buffer diff --git a/interface/driver_ws2812b_interface_template.c b/interface/driver_ws2812b_interface_template.c index 5063d17..4d56097 100644 --- a/interface/driver_ws2812b_interface_template.c +++ b/interface/driver_ws2812b_interface_template.c @@ -37,13 +37,13 @@ #include "driver_ws2812b_interface.h" /** - * @brief interface spi 10mhz bus init + * @brief interface spi bus init * @return status code * - 0 success - * - 1 spi init 10mhz failed + * - 1 spi init failed * @note none */ -uint8_t ws2812b_interface_spi_10mhz_init(void) +uint8_t ws2812b_interface_spi_init(void) { return 0; } @@ -60,6 +60,24 @@ uint8_t ws2812b_interface_spi_deinit(void) return 0; } +/** + * @brief WS1812B One Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_one_code(void) +{ + return 0xFF00U; +} + +/** + * @brief WS1812B Zero Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_zero_code(void) +{ + return 0xF000U; +} + /** * @brief interface spi bus write command * @param[in] *buf points to a data buffer diff --git a/project/odroidc4/CMakeLists.txt b/project/odroidc4/CMakeLists.txt new file mode 100644 index 0000000..15c73c6 --- /dev/null +++ b/project/odroidc4/CMakeLists.txt @@ -0,0 +1,212 @@ +# +# Copyright (c) 2015 - present LibDriver All rights reserved +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +# set the cmake minimum version +cmake_minimum_required(VERSION 3.0) + +# set the project name and language +project(ws2812b C) + +# read the version from files +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/VERSION ${CMAKE_PROJECT_NAME}_VERSION) + +# set the project version +set(PROJECT_VERSION ${${CMAKE_PROJECT_NAME}_VERSION}) + +# set c standard c99 +set(CMAKE_C_STANDARD 99) + +# enable c standard required +set(CMAKE_C_STANDARD_REQUIRED True) + +# set release level +# set(CMAKE_BUILD_TYPE Release) + +# set the release flags of c +set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") + +# include cmake package config helpers +include(CMakePackageConfigHelpers) + +# find the pkgconfig and use this tool to find the third party packages +find_package(PkgConfig REQUIRED) + +# find the third party packages with pkgconfig +pkg_search_module(GPIOD REQUIRED libgpiod) + +# include all library header directories +set(LIB_INC_DIRS + ${GPIOD_INCLUDE_DIRS} + ) + +# include all linked libraries +set(LIBS + ${GPIOD_LIBRARIES} + ) + +# include all header directories +set(INC_DIRS + ${LIB_INC_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../src + ${CMAKE_CURRENT_SOURCE_DIR}/../../interface + ${CMAKE_CURRENT_SOURCE_DIR}/../../example + ${CMAKE_CURRENT_SOURCE_DIR}/../../test + ${CMAKE_CURRENT_SOURCE_DIR}/interface/inc + ) + +# include all installed headers +file(GLOB INSTL_INCS + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.h + ) + +# include all sources files +file(GLOB SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.c + ) + +# include executable source +file(GLOB MAIN + ${SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../example/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../test/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/interface/src/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/driver/src/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ) + +# enable output as a static library +add_library(${CMAKE_PROJECT_NAME}_static STATIC ${SRCS}) + +# set the static library include directories +target_include_directories(${CMAKE_PROJECT_NAME}_static PRIVATE ${INC_DIRS}) + +# set the static library link libraries +target_link_libraries(${CMAKE_PROJECT_NAME}_static + m + ) + +# rename as ${CMAKE_PROJECT_NAME} +set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME}) + +# don't delete ${CMAKE_PROJECT_NAME} libs +set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# set the static library version +set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES VERSION ${${CMAKE_PROJECT_NAME}_VERSION}) + +# enable output as a dynamic library +add_library(${CMAKE_PROJECT_NAME} SHARED ${SRCS}) + +# set the executable program include directories +target_include_directories(${CMAKE_PROJECT_NAME} + PUBLIC $ + PRIVATE ${INC_DIRS} + ) + +# set the dynamic library link libraries +target_link_libraries(${CMAKE_PROJECT_NAME} + m + ) + +# rename as ${CMAKE_PROJECT_NAME} +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME}) + +# don't delete ${CMAKE_PROJECT_NAME} libs +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# include the public header +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${INSTL_INCS}") + +# set the dynamic library version +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES VERSION ${${CMAKE_PROJECT_NAME}_VERSION}) + +# enable the executable program +add_executable(${CMAKE_PROJECT_NAME}_exe ${MAIN}) + +# set the executable program include directories +target_include_directories(${CMAKE_PROJECT_NAME}_exe PRIVATE ${INC_DIRS}) + +# set the executable program link libraries +target_link_libraries(${CMAKE_PROJECT_NAME}_exe + ${LIBS} + m + pthread + ) + +# rename as ${CMAKE_PROJECT_NAME} +set_target_properties(${CMAKE_PROJECT_NAME}_exe PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME}) + +# don't delete ${CMAKE_PROJECT_NAME} exe +set_target_properties(${CMAKE_PROJECT_NAME}_exe PROPERTIES CLEAN_DIRECT_OUTPUT 1) + +# install the binary +install(TARGETS ${CMAKE_PROJECT_NAME}_exe + RUNTIME DESTINATION bin + ) + +# install the static library +install(TARGETS ${CMAKE_PROJECT_NAME}_static + ARCHIVE DESTINATION lib + ) + +# install the dynamic library +install(TARGETS ${CMAKE_PROJECT_NAME} + EXPORT ${CMAKE_PROJECT_NAME}-targets + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include/${CMAKE_PROJECT_NAME} + ) + +# make the cmake config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}-config.cmake + INSTALL_DESTINATION cmake + ) + +# write the cmake config version +write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}-config-version.cmake + VERSION ${PACKAGE_VERSION} + COMPATIBILITY AnyNewerVersion + ) + +# install the cmake files +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}-config-version.cmake" + DESTINATION cmake + ) + +# set the export items +install(EXPORT ${CMAKE_PROJECT_NAME}-targets + DESTINATION cmake + ) + +# add uninstall command +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake + ) + +#include ctest module +include(CTest) + +# creat a test +add_test(NAME ${CMAKE_PROJECT_NAME}_test COMMAND ${CMAKE_PROJECT_NAME}_exe -p) diff --git a/project/odroidc4/J8Header.jpg b/project/odroidc4/J8Header.jpg new file mode 100644 index 0000000000000000000000000000000000000000..62d1476f43224fafbb9d143ea39ef19ff165badc GIT binary patch literal 89332 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 literal 0 HcmV?d00001 diff --git a/project/odroidc4/Makefile b/project/odroidc4/Makefile new file mode 100644 index 0000000..be59175 --- /dev/null +++ b/project/odroidc4/Makefile @@ -0,0 +1,149 @@ +# +# Copyright (c) 2015 - present LibDriver All rights reserved +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +# set the project version +VERSION := 1.0.0 + +# set the application name +APP_NAME := ws2812b + +# set the shared libraries name +SHARED_LIB_NAME := libws2812b.so + +# set the static libraries name +STATIC_LIB_NAME := libws2812b.a + +# set the install directories +INSTL_DIRS := /usr/local + +# set the include directories +INC_INSTL_DIRS := $(INSTL_DIRS)/include/$(APP_NAME) + +# set the library directories +LIB_INSTL_DIRS := $(INSTL_DIRS)/lib + +# set the bin directories +BIN_INSTL_DIRS := $(INSTL_DIRS)/bin + +# set the compiler +CC := gcc + +# set the ar tool +AR := ar + +# set the packages name +PKGS := libgpiod + +# set the pck-config header directories +LIB_INC_DIRS := $(shell pkg-config --cflags $(PKGS)) + +# set the linked libraries +LIBS := -lm \ + -lpthread + +# add the linked libraries +LIBS += $(shell pkg-config --libs $(PKGS)) + +# set all header directories +INC_DIRS := -I ../../src/ \ + -I ../../interface/ \ + -I ../../example/ \ + -I ../../test/ \ + -I ./interface/inc/ + +# add the linked libraries header directories +INC_DIRS += $(LIB_INC_DIRS) + +# set the installing headers +INSTL_INCS := $(wildcard ../../src/*.h) + +# set all sources files +SRCS := $(wildcard ../../src/*.c) + +# set the main source +MAIN := $(SRCS) \ + $(wildcard ../../example/*.c) \ + $(wildcard ../../test/*.c) \ + $(wildcard ./interface/src/*.c) \ + $(wildcard ./driver/src/*.c) \ + $(wildcard ./src/main.c) + +# set flags of the compiler +CFLAGS := -O3 \ + -DNDEBUG + +# set all .PHONY +.PHONY: all + +# set the output list +all: $(APP_NAME) $(SHARED_LIB_NAME).$(VERSION) $(STATIC_LIB_NAME) + +# set the main app +$(APP_NAME) : $(MAIN) + $(CC) $(CFLAGS) $^ $(INC_DIRS) $(LIBS) -o $@ + +# set the shared lib +$(SHARED_LIB_NAME).$(VERSION) : $(SRCS) + $(CC) $(CFLAGS) -shared -fPIC $^ $(INC_DIRS) -lm -o $@ + +# set the *.o for the static libraries +OBJS := $(patsubst %.c, %.o, $(SRCS)) + +# set the static lib +$(STATIC_LIB_NAME) : $(OBJS) + $(AR) -r $@ $^ + +# .*o used by the static lib +$(OBJS) : $(SRCS) + $(CC) $(CFLAGS) -c $^ $(INC_DIRS) -o $@ + +# set install .PHONY +.PHONY: install + +# install files +install : + $(shell if [ ! -d $(INC_INSTL_DIRS) ]; then mkdir $(INC_INSTL_DIRS); fi;) + cp -rv $(INSTL_INCS) $(INC_INSTL_DIRS) + cp -rv $(SHARED_LIB_NAME).$(VERSION) $(LIB_INSTL_DIRS) + ln -sf $(LIB_INSTL_DIRS)/$(SHARED_LIB_NAME).$(VERSION) $(LIB_INSTL_DIRS)/$(SHARED_LIB_NAME) + cp -rv $(STATIC_LIB_NAME) $(LIB_INSTL_DIRS) + cp -rv $(APP_NAME) $(BIN_INSTL_DIRS) + +# set install .PHONY +.PHONY: uninstall + +# uninstall files +uninstall : + rm -rf $(INC_INSTL_DIRS) + rm -rf $(LIB_INSTL_DIRS)/$(SHARED_LIB_NAME).$(VERSION) + rm -rf $(LIB_INSTL_DIRS)/$(SHARED_LIB_NAME) + rm -rf $(LIB_INSTL_DIRS)/$(STATIC_LIB_NAME) + rm -rf $(BIN_INSTL_DIRS)/$(APP_NAME) + +# set clean .PHONY +.PHONY: clean + +# clean the project +clean : + rm -rf $(APP_NAME) $(SHARED_LIB_NAME).$(VERSION) $(STATIC_LIB_NAME) diff --git a/project/odroidc4/README.md b/project/odroidc4/README.md new file mode 100644 index 0000000..92427c4 --- /dev/null +++ b/project/odroidc4/README.md @@ -0,0 +1,184 @@ +### 1. Board + +#### 1.1 Board Info + +Board Name: Odroid C4. + +SPI Pin: SCLK/MOSI/MISO/CS GPIOX11/GPIOX8/GPIOX9/GPIOX10. + +### 2. Install + +#### 2.1 Dependencies + +Install the necessary dependencies. + +```shell +sudo apt-get install libgpiod-dev pkg-config cmake -y +``` + +#### 2.2 Makefile + +Build the project. + +```shell +make +``` + +Install the project and this is optional. + +```shell +sudo make install +``` + +Uninstall the project and this is optional. + +```shell +sudo make uninstall +``` + +#### 2.3 CMake + +Build the project. + +```shell +mkdir build && cd build +cmake .. +make +``` + +Install the project and this is optional. + +```shell +sudo make install +``` + +Uninstall the project and this is optional. + +```shell +sudo make uninstall +``` + +Test the project and this is optional. + +```shell +make test +``` + +Find the compiled library in CMake. + +```cmake +find_package(ws2812b REQUIRED) +``` + + +### 3. WS2812B + +#### 3.1 Command Instruction + +1. Show ws2812b chip and driver information. + + ```shell + ws2812b (-i | --information) + ``` + +2. Show ws2812b help. + + ```shell + ws2812b (-h | --help) + ``` + +3. Show ws2812b pin connections of the current board. + + ```shell + ws2812b (-p | --port) + ``` + +4. Run ws2812b write test, number is the ws2812b chip number and num is the test times. + + ```shell + ws2812b (-t write | --test=write) [--number=] [--times=] + ``` + +5. Run ws2812b write function, number is the ws2812b chip number and color is the rgb color. + + ```shell + ws2812b (-e write | --example=write) [--number=] [--color=] + ``` + +#### 3.2 Command Example + +```shell +./ws2812b -i + +ws2812b: chip is Worldsemi WS2812B. +ws2812b: manufacturer is Worldsemi . +ws2812b: interface is SPI. +ws2812b: driver version is 1.0. +ws2812b: min supply voltage is 3.7V. +ws2812b: max supply voltage is 5.3V. +ws2812b: max current is 16.00mA. +ws2812b: max temperature is 85.0C. +``` + +```shell +./ws2812b -p + +ws2812b: SPI interface SCK connected to GPIO11(BCM). +ws2812b: SPI interface MISO connected to GPIO9(BCM). +ws2812b: SPI interface MOSI connected to GPIO10(BCM). +ws2812b: SPI interface CS connected to GPIO8(BCM). +``` + +```shell +./ws2812b -t write --number=12 --times=10 + +ws2812b: chip is Worldsemi WS2812B. +ws2812b: manufacturer is Worldsemi . +ws2812b: interface is SPI. +ws2812b: driver version is 1.0. +ws2812b: min supply voltage is 3.7V. +ws2812b: max supply voltage is 5.3V. +ws2812b: max current is 16.00mA. +ws2812b: max temperature is 85.0C. +ws2812b: min temperature is -25.0C. +ws2812b: start register test. +ws2812b: 1/10 times. +ws2812b: 2/10 times. +ws2812b: 3/10 times. +ws2812b: 4/10 times. +ws2812b: 5/10 times. +ws2812b: 6/10 times. +ws2812b: 7/10 times. +ws2812b: 8/10 times. +ws2812b: 9/10 times. +ws2812b: 10/10 times. +ws2812b: finish register test. +``` + +```shell +./ws2812b -e write --number=12 --color=16711680 + +ws2812b: number is 12 and written color is 0xFF0000. +``` + +```shell +./ws2812b -h + +Usage: + ws2812b (-i | --information) + ws2812b (-h | --help) + ws2812b (-p | --port) + ws2812b (-t write | --test=write) [--number=] [--times=] + ws2812b (-e write | --example=write) [--number=] [--color=] + +Options: + --color= Set the chip display color.([default: 16711680]) + -e , --example= Run the driver example. + -h, --help Show the help. + -i, --information Show the chip information. + --number= Set the chip number.([default: 3]) + -p, --port Display the pin connections of the current board. + -t , --test= Run the driver test. + --times= Set the running times.([default: 3]) +``` + diff --git a/project/odroidc4/cmake/VERSION b/project/odroidc4/cmake/VERSION new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/project/odroidc4/cmake/VERSION @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/project/odroidc4/cmake/config.cmake.in b/project/odroidc4/cmake/config.cmake.in new file mode 100644 index 0000000..a982dfe --- /dev/null +++ b/project/odroidc4/cmake/config.cmake.in @@ -0,0 +1,44 @@ +# +# Copyright (c) 2015 - present LibDriver All rights reserved +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +# set the package init +@PACKAGE_INIT@ + +# include dependency macro +include(CMakeFindDependencyMacro) + +# find the pkgconfig and use this tool to find the third party packages +find_package(PkgConfig REQUIRED) + +# find the third party packages with pkgconfig +pkg_search_module(GPIOD REQUIRED libgpiod) + +# include the cmake targets +include(${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets.cmake) + +# get the include header directories +get_target_property(@CMAKE_PROJECT_NAME@_INCLUDE_DIRS @CMAKE_PROJECT_NAME@ INTERFACE_INCLUDE_DIRECTORIES) + +# get the library directories +get_target_property(@CMAKE_PROJECT_NAME@_LIBRARIES @CMAKE_PROJECT_NAME@ IMPORTED_LOCATION_RELEASE) diff --git a/project/odroidc4/cmake/uninstall.cmake b/project/odroidc4/cmake/uninstall.cmake new file mode 100644 index 0000000..409e6a8 --- /dev/null +++ b/project/odroidc4/cmake/uninstall.cmake @@ -0,0 +1,58 @@ +# +# Copyright (c) 2015 - present LibDriver All rights reserved +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +# check the install_manifest.txt +if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") + # output the error + message(FATAL_ERROR "cannot find install manifest: ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") +endif() + +# read install_manifest.txt to uninstall_list +file(READ "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt" ${CMAKE_PROJECT_NAME}_uninstall_list) + +# replace '\n' to ';' +string(REGEX REPLACE "\n" ";" ${CMAKE_PROJECT_NAME}_uninstall_list "${${CMAKE_PROJECT_NAME}_uninstall_list}") + +# uninstall the list files +foreach(${CMAKE_PROJECT_NAME}_uninstall_list ${${CMAKE_PROJECT_NAME}_uninstall_list}) + # if a link or a file + if(IS_SYMLINK "$ENV{DESTDIR}${${CMAKE_PROJECT_NAME}_uninstall_list}" OR EXISTS "$ENV{DESTDIR}${${CMAKE_PROJECT_NAME}_uninstall_list}") + # delete the file + execute_process(COMMAND ${CMAKE_COMMAND} -E remove ${${CMAKE_PROJECT_NAME}_uninstall_list} + RESULT_VARIABLE rm_retval + ) + + # check the retval + if(NOT "${rm_retval}" STREQUAL 0) + # output the error + message(FATAL_ERROR "failed to remove file: '${${CMAKE_PROJECT_NAME}_uninstall_list}'.") + else() + # uninstalling files + message(STATUS "uninstalling: $ENV{DESTDIR}${${CMAKE_PROJECT_NAME}_uninstall_list}") + endif() + else() + # output the error + message(STATUS "file: $ENV{DESTDIR}${${CMAKE_PROJECT_NAME}_uninstall_list} does not exist.") + endif() +endforeach() diff --git a/project/odroidc4/driver/src/odroidc4_driver_ws2812b_interface.c b/project/odroidc4/driver/src/odroidc4_driver_ws2812b_interface.c new file mode 100644 index 0000000..bf3ce19 --- /dev/null +++ b/project/odroidc4/driver/src/odroidc4_driver_ws2812b_interface.c @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file odroidc4_driver_ws2812b_interface.c + * @brief odroidc4 driver ws2812b interface source file + * @version 1.0.0 + * @author Shifeng Li + * @date 2021-11-13 + * + *

history

+ * + *
Date Version Author Description + *
2021/11/13 1.0 Shifeng Li first upload + *
+ */ + +#include "driver_ws2812b_interface.h" +#include "spi.h" +#include + +/** + * @brief spi device name definition + */ +#define SPI_DEVICE_NAME "/dev/spidev0.0" /**< spi device name */ +/** + * @brief spi device handle definition + */ +static int gs_fd; /**< spi handle */ + +/** + * @brief interface spi bus init + * @return status code + * - 0 success + * - 1 spi init failed + * @note none + */ +uint8_t ws2812b_interface_spi_init(void) +{ + return spi_init(SPI_DEVICE_NAME, &gs_fd, SPI_MODE_TYPE_3, 1000 * 1000 * 12); +} + +/** + * @brief interface spi bus deinit + * @return status code + * - 0 success + * - 1 spi deinit failed + * @note none + */ +uint8_t ws2812b_interface_spi_deinit(void) +{ + return spi_deinit(gs_fd); +} + +/** + * @brief WS1812B One Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_one_code(void) +{ + return 0xFF00U; +} + +/** + * @brief WS1812B Zero Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_zero_code(void) +{ + return 0xF000U; +} + +/** + * @brief interface spi bus write command + * @param[in] *buf points to a data buffer + * @param[in] len is the length of data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t ws2812b_interface_spi_write_cmd(uint8_t *buf, uint16_t len) +{ + return spi_write_cmd(gs_fd, buf, len); +} + +/** + * @brief interface delay ms + * @param[in] ms + * @note none + */ +void ws2812b_interface_delay_ms(uint32_t ms) +{ + usleep(1000 * ms); +} + +/** + * @brief interface print format data + * @param[in] fmt is the format data + * @note none + */ +void ws2812b_interface_debug_print(const char *const fmt, ...) +{ + char str[256]; + uint16_t len; + va_list args; + + memset((char *)str, 0, sizeof(char) * 256); + va_start(args, fmt); + vsnprintf((char *)str, 255, (char const *)fmt, args); + va_end(args); + + len = strlen((char *)str); + (void)printf((uint8_t *)str, len); +} diff --git a/project/odroidc4/interface/inc/spi.h b/project/odroidc4/interface/inc/spi.h new file mode 100644 index 0000000..e25318d --- /dev/null +++ b/project/odroidc4/interface/inc/spi.h @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file spi.h + * @brief spi header file + * @version 1.0.0 + * @author Shifeng Li + * @date 2022-11-11 + * + *

history

+ * + *
Date Version Author Description + *
2022/11/11 1.0 Shifeng Li first upload + *
+ */ + +#ifndef SPI_H +#define SPI_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup spi spi function + * @brief spi function modules + * @{ + */ + +/** + * @brief spi mode type enumeration definition + */ +typedef enum +{ + SPI_MODE_TYPE_0 = SPI_MODE_0, /**< mode 0 */ + SPI_MODE_TYPE_1 = SPI_MODE_1, /**< mode 1 */ + SPI_MODE_TYPE_2 = SPI_MODE_2, /**< mode 2 */ + SPI_MODE_TYPE_3 = SPI_MODE_3, /**< mode 3 */ +} spi_mode_type_t; + +/** + * @brief spi bus init + * @param[in] *name points to a spi device name buffer + * @param[out] *fd points to a spi device handle buffer + * @param[in] mode is the spi mode. + * @param[in] freq is the spi running frequence + * @return status code + * - 0 success + * - 1 init failed + * @note none + */ +uint8_t spi_init(char *name, int *fd, spi_mode_type_t mode, uint32_t freq); + +/** + * @brief spi bus deinit + * @param[in] fd is the spi handle + * @return status code + * - 0 success + * - 1 deinit failed + * @note none + */ +uint8_t spi_deinit(int fd); + +/** + * @brief spi bus read command + * @param[in] fd is the spi handle + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read_cmd(int fd, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus read + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read(int fd, uint8_t reg, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus read address 16 + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read_address16(int fd, uint16_t reg, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus write command + * @param[in] fd is the spi handle + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write_cmd(int fd, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus write + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write(int fd, uint8_t reg, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus write address 16 + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write_address16(int fd, uint16_t reg, uint8_t *buf, uint16_t len); + +/** + * @brief spi bus write read + * @param[in] fd is the spi handle + * @param[in] *in_buf points to an input buffer + * @param[in] in_len is the input length + * @param[out] *out_buf points to an output buffer + * @param[in] out_len is the output length + * @return status code + * - 0 success + * - 1 write read failed + * @note none + */ +uint8_t spi_write_read(int fd, uint8_t *in_buf, uint32_t in_len, uint8_t *out_buf, uint32_t out_len); + +/** + * @brief spi transmit + * @param[in] fd is the spi handle + * @param[in] *tx points to a tx buffer + * @param[out] *rx points to a rx buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 transmit failed + * @note none + */ +uint8_t spi_transmit(int fd, uint8_t *tx, uint8_t *rx, uint16_t len); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/project/odroidc4/interface/src/spi.c b/project/odroidc4/interface/src/spi.c new file mode 100644 index 0000000..08023d5 --- /dev/null +++ b/project/odroidc4/interface/src/spi.c @@ -0,0 +1,514 @@ +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file spi.c + * @brief spi source file + * @version 1.0.0 + * @author Shifeng Li + * @date 2022-11-11 + * + *

history

+ * + *
Date Version Author Description + *
2022/11/11 1.0 Shifeng Li first upload + *
+ */ + +#include "spi.h" +#include +#include +#include + +/** + * @brief spi bus init + * @param[in] *name points to a spi device name buffer + * @param[out] *fd points to a spi device handle buffer + * @param[in] mode is the spi mode. + * @param[in] freq is the spi running frequence + * @return status code + * - 0 success + * - 1 init failed + * @note none + */ +uint8_t spi_init(char *name, int *fd, spi_mode_type_t mode, uint32_t freq) +{ + int i; + + /* open the spi device */ + *fd = open(name, O_RDWR); + if ((*fd) < 0) + { + perror("spi: open failed.\n"); + + return 1; + } + else + { + /* set the spi write mode */ + i = mode; + if (ioctl(*fd, SPI_IOC_WR_MODE, &i) < 0) + { + perror("spi: write mode set failed.\n"); + + return 1; + } + + /* set the spi read mode */ + if (ioctl(*fd, SPI_IOC_RD_MODE, &i) < 0) + { + perror("spi: read mode set failed.\n"); + + return 1; + } + + /* set the spi write frequence */ + i = freq; + if (ioctl(*fd, SPI_IOC_WR_MAX_SPEED_HZ, &i) < 0) + { + perror("spi: set spi write speed failed.\n"); + + return 1; + } + + /* set the spi read frequence */ + if (ioctl(*fd, SPI_IOC_RD_MAX_SPEED_HZ, &i) < 0) + { + perror("spi: set spi read speed failed.\n"); + + return 1; + } + + /* set the spi write MSB first */ + i = 0; + if (ioctl(*fd, SPI_IOC_WR_LSB_FIRST, &i) < 0) + { + perror("spi: set spi write msb first failed.\n"); + + return 1; + } + + /* set the spi read MSB first */ + if (ioctl(*fd, SPI_IOC_RD_LSB_FIRST, &i) < 0) + { + perror("spi: set spi read msb first failed.\n"); + + return 1; + } + + /* set the spi write 8 bits mode */ + i = 8; + if (ioctl(*fd, SPI_IOC_WR_BITS_PER_WORD, &i) < 0) + { + perror("spi: set spi wirte 8 bit failed.\n"); + + return 1; + } + + /* set the spi read 8 bits mode */ + if (ioctl(*fd, SPI_IOC_RD_BITS_PER_WORD, &i) < 0) + { + perror("spi: set spi read 8 bit failed.\n"); + + return 1; + } + + return 0; + } +} + +/** + * @brief spi bus deinit + * @param[in] fd is the spi handle + * @return status code + * - 0 success + * - 1 deinit failed + * @note none + */ +uint8_t spi_deinit(int fd) +{ + /* close the spi */ + if (close(fd) < 0) + { + perror("spi: close failed.\n"); + + return 1; + } + else + { + return 0; + } +} + +/** + * @brief spi bus read command + * @param[in] fd is the spi handle + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read_cmd(int fd, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + int l; + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.rx_buf = (unsigned long)buf; + k.len = len; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; +} + +/** + * @brief spi bus read + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read(int fd, uint8_t reg, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + uint8_t command[len + 1]; + uint8_t recv[len + 1]; + int l; + + /* set the command */ + command[0] = reg; + memset(&command[1], 0x00, len); + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)command; + k.rx_buf = (unsigned long)recv; + k.len = len + 1; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + /* copy the data */ + memcpy(buf, recv + 1, len); + + return 0; +} + +/** + * @brief spi bus read address 16 + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[out] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 read failed + * @note none + */ +uint8_t spi_read_address16(int fd, uint16_t reg, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + uint8_t command[len + 2]; + uint8_t recv[len + 2]; + int l; + + /* set the command */ + command[0] = (reg >> 8) & 0xFF; + command[1] = reg & 0xFF; + memset(&command[2], 0x00, len); + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)command; + k.rx_buf = (unsigned long)recv; + k.len = len + 2; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + /* copy the data */ + memcpy(buf, recv + 2, len); + + return 0; +} + +/** + * @brief spi bus write command + * @param[in] fd is the spi handle + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write_cmd(int fd, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + int l; + + /* clear ioc transfer */ + memset(&k, 0, sizeof(k)); + + /* set the param */ + k.tx_buf = (unsigned long)buf; + k.len = len; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; +} + +/** + * @brief spi bus write + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write(int fd, uint8_t reg, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + uint8_t command[len + 1]; + int l; + + /* set the command */ + command[0] = reg; + memcpy(&command[1], buf, len); + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)command; + k.len = len + 1; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; +} + +/** + * @brief spi bus write address 16 + * @param[in] fd is the spi handle + * @param[in] reg is the spi register address + * @param[in] *buf points to a data buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 write failed + * @note none + */ +uint8_t spi_write_address16(int fd, uint16_t reg, uint8_t *buf, uint16_t len) +{ + struct spi_ioc_transfer k; + uint8_t command[len + 2]; + int l; + + /* set the command */ + command[0] = (reg >> 8) & 0xFF; + command[1] = reg & 0xFF; + memcpy(&command[2], buf, len); + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)command; + k.len = len + 2; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; +} + +/** + * @brief spi bus write read + * @param[in] fd is the spi handle + * @param[in] *in_buf points to an input buffer + * @param[in] in_len is the input length + * @param[out] *out_buf points to an output buffer + * @param[in] out_len is the output length + * @return status code + * - 0 success + * - 1 write read failed + * @note none + */ +uint8_t spi_write_read(int fd, uint8_t *in_buf, uint32_t in_len, uint8_t *out_buf, uint32_t out_len) +{ + if ((in_len > 0) && (out_len > 0)) + { + struct spi_ioc_transfer k[2]; + int l; + + /* clear ioc transfer */ + memset(k, 0, sizeof(struct spi_ioc_transfer) * 2); + + /* set the param */ + k[0].tx_buf = (unsigned long)in_buf; + k[0].len = in_len; + k[0].cs_change = 0; + k[1].rx_buf = (unsigned long)out_buf; + k[1].len = out_len; + k[1].cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(2), &k); + if (l != (k[0].len + k[1].len)) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; + } + else if (in_len > 0) + { + struct spi_ioc_transfer k; + int l; + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)in_buf; + k.len = in_len; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; + } + else + { + return 1; + } +} + +/** + * @brief spi transmit + * @param[in] fd is the spi handle + * @param[in] *tx points to a tx buffer + * @param[out] *rx points to a rx buffer + * @param[in] len is the length of the data buffer + * @return status code + * - 0 success + * - 1 transmit failed + * @note none + */ +uint8_t spi_transmit(int fd, uint8_t *tx, uint8_t *rx, uint16_t len) +{ + int l; + struct spi_ioc_transfer k; + + /* clear ioc transfer */ + memset(&k, 0, sizeof(struct spi_ioc_transfer)); + + /* set the param */ + k.tx_buf = (unsigned long)tx; + k.rx_buf = (unsigned long)rx; + k.len = len; + k.cs_change = 0; + + /* transmit */ + l = ioctl(fd, SPI_IOC_MESSAGE(1), &k); + if (l != k.len) + { + perror("spi: length check error.\n"); + + return 1; + } + + return 0; +} diff --git a/project/odroidc4/src/main.c b/project/odroidc4/src/main.c new file mode 100644 index 0000000..5ed1106 --- /dev/null +++ b/project/odroidc4/src/main.c @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2015 - present LibDriver All rights reserved + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * @file main.c + * @brief main source file + * @version 1.0.0 + * @author Shifeng Li + * @date 2021-11-13 + * + *

history

+ * + *
Date Version Author Description + *
2021/11/13 1.0 Shifeng Li first upload + *
+ */ + +#include "driver_ws2812b_write_test.h" +#include "driver_ws2812b_basic.h" +#include +#include + +#define LED_COUNT 24 +#define TEMP_BUF_SIZE 1152 + +/** + * @brief global var definition + */ +static uint32_t gs_rgb[LED_COUNT]; /**< rgb buffer */ +static uint8_t gs_temp[TEMP_BUF_SIZE]; /**< temp buffer*/ + +/** + * @brief ws2812b full function + * @param[in] argc is arg numbers + * @param[in] **argv is the arg address + * @return status code + * - 0 success + * - 1 run failed + * - 5 param is invalid + * @note none + */ +uint8_t ws2812b(uint8_t argc, char **argv) +{ + int c; + int longindex = 0; + const char short_options[] = "hipe:t:"; + const struct option long_options[] = + { + {"help", no_argument, NULL, 'h'}, + {"information", no_argument, NULL, 'i'}, + {"port", no_argument, NULL, 'p'}, + {"example", required_argument, NULL, 'e'}, + {"test", required_argument, NULL, 't'}, + {"color", required_argument, NULL, 1}, + {"number", required_argument, NULL, 2}, + {"times", required_argument, NULL, 3}, + {NULL, 0, NULL, 0}, + }; + char type[33] = "unknown"; + uint32_t times = 3; + uint32_t color = 16711680; + uint32_t number = 3; + + /* if no params */ + if (argc == 1) + { + /* goto the help */ + goto help; + } + + /* init 0 */ + optind = 0; + + /* parse */ + do + { + /* parse the args */ + c = getopt_long(argc, argv, short_options, long_options, &longindex); + + /* judge the result */ + switch (c) + { + /* help */ + case 'h' : + { + /* set the type */ + memset(type, 0, sizeof(char) * 33); + snprintf(type, 32, "h"); + + break; + } + + /* information */ + case 'i' : + { + /* set the type */ + memset(type, 0, sizeof(char) * 33); + snprintf(type, 32, "i"); + + break; + } + + /* port */ + case 'p' : + { + /* set the type */ + memset(type, 0, sizeof(char) * 33); + snprintf(type, 32, "p"); + + break; + } + + /* example */ + case 'e' : + { + /* set the type */ + memset(type, 0, sizeof(char) * 33); + snprintf(type, 32, "e_%s", optarg); + + break; + } + + /* test */ + case 't' : + { + /* set the type */ + memset(type, 0, sizeof(char) * 33); + snprintf(type, 32, "t_%s", optarg); + + break; + } + + /* color */ + case 1 : + { + /* set the color */ + color = atol(optarg); + + break; + } + + /* number */ + case 2 : + { + /* set the number */ + number = atoi(optarg); + + break; + } + + /* running times */ + case 3 : + { + /* set the times */ + times = atol(optarg); + + break; + } + + /* the end */ + case -1 : + { + break; + } + + /* others */ + default : + { + return 5; + } + } + } while (c != -1); + + /* run the function */ + if (strcmp("t_write", type) == 0) + { + uint8_t res; + + /* run write test */ + res = ws2812b_write_test(number, times); + if (res != 0) + { + return 1; + } + else + { + return 0; + } + } + else if (strcmp("e_write", type) == 0) + { + uint8_t res; + uint32_t i; + + /* check number */ + if (number > LED_COUNT) + { + number = LED_COUNT; + ws2812b_interface_debug_print("ws2812b: num is over %d and use %d.\n", LED_COUNT, LED_COUNT); + } + + /* init */ + res = ws2812b_basic_init(); + if (res != 0) + { + return 1; + } + + /* write color */ + for (i = 0; i < number; i++) + { + gs_rgb[i] = color; + } + + /* print */ + ws2812b_interface_debug_print("ws2812b: number is %d and written color is 0x%X.\n", number, color); + + /* write data */ + res = ws2812b_basic_write(gs_rgb, number, gs_temp, TEMP_BUF_SIZE); + if (res != 0) + { + (void)ws2812b_basic_deinit(); + + return 1; + } + + /* close the chip */ + res = ws2812b_basic_deinit(); + if (res != 0) + { + return 1; + } + else + { + return 0; + } + } + else if (strcmp("h", type) == 0) + { + help: + ws2812b_interface_debug_print("Usage:\n"); + ws2812b_interface_debug_print(" ws2812b (-i | --information)\n"); + ws2812b_interface_debug_print(" ws2812b (-h | --help)\n"); + ws2812b_interface_debug_print(" ws2812b (-p | --port)\n"); + ws2812b_interface_debug_print(" ws2812b (-t write | --test=write) [--number=] [--times=]\n"); + ws2812b_interface_debug_print(" ws2812b (-e write | --example=write) [--number=] [--color=]\n"); + ws2812b_interface_debug_print("\n"); + ws2812b_interface_debug_print("Options:\n"); + ws2812b_interface_debug_print(" --color= Set the chip display color.([default: 16711680])\n"); + ws2812b_interface_debug_print(" -e , --example= Run the driver example.\n"); + ws2812b_interface_debug_print(" -h, --help Show the help.\n"); + ws2812b_interface_debug_print(" -i, --information Show the chip information.\n"); + ws2812b_interface_debug_print(" --number= Set the chip number.([default: 3])\n"); + ws2812b_interface_debug_print(" -p, --port Display the pin connections of the current board.\n"); + ws2812b_interface_debug_print(" -t , --test= Run the driver test.\n"); + ws2812b_interface_debug_print(" --times= Set the running times.([default: 3])\n"); + + return 0; + } + else if (strcmp("i", type) == 0) + { + ws2812b_info_t info; + + /* print ws2812b info */ + ws2812b_info(&info); + ws2812b_interface_debug_print("ws2812b: chip is %s.\n", info.chip_name); + ws2812b_interface_debug_print("ws2812b: manufacturer is %s.\n", info.manufacturer_name); + ws2812b_interface_debug_print("ws2812b: interface is %s.\n", info.interface); + ws2812b_interface_debug_print("ws2812b: driver version is %d.%d.\n", info.driver_version / 1000, (info.driver_version % 1000) / 100); + ws2812b_interface_debug_print("ws2812b: min supply voltage is %0.1fV.\n", info.supply_voltage_min_v); + ws2812b_interface_debug_print("ws2812b: max supply voltage is %0.1fV.\n", info.supply_voltage_max_v); + ws2812b_interface_debug_print("ws2812b: max current is %0.2fmA.\n", info.max_current_ma); + ws2812b_interface_debug_print("ws2812b: max temperature is %0.1fC.\n", info.temperature_max); + ws2812b_interface_debug_print("ws2812b: min temperature is %0.1fC.\n", info.temperature_min); + + return 0; + } + else if (strcmp("p", type) == 0) + { + /* print pin connection */ + ws2812b_interface_debug_print("ws2812b: SPI interface SCK connected to GPIOX11(BCM).\n"); + ws2812b_interface_debug_print("ws2812b: SPI interface MISO connected to GPIOX9(BCM).\n"); + ws2812b_interface_debug_print("ws2812b: SPI interface MOSI connected to GPIOX8(BCM).\n"); + ws2812b_interface_debug_print("ws2812b: SPI interface CS connected to GPIOX10(BCM).\n"); + + return 0; + } + else + { + return 5; + } +} + +/** + * @brief main function + * @param[in] argc is arg numbers + * @param[in] **argv is the arg address + * @return status code + * - 0 success + * @note none + */ +int main(uint8_t argc, char **argv) +{ + uint8_t res; + + res = ws2812b(argc, argv); + if (res == 0) + { + /* run success */ + } + else if (res == 1) + { + ws2812b_interface_debug_print("ws2812b: run failed.\n"); + } + else if (res == 5) + { + ws2812b_interface_debug_print("ws2812b: param is invalid.\n"); + } + else + { + ws2812b_interface_debug_print("ws2812b: unknown status code.\n"); + } + + return 0; +} diff --git a/project/raspberrypi4b/CMakeLists.txt b/project/raspberrypi4b/CMakeLists.txt index eb24398..2a006a9 100644 --- a/project/raspberrypi4b/CMakeLists.txt +++ b/project/raspberrypi4b/CMakeLists.txt @@ -205,8 +205,8 @@ add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake ) -#include ctest module -include(CTest) +# #include ctest module +# include(CTest) -# creat a test -add_test(NAME ${CMAKE_PROJECT_NAME}_test COMMAND ${CMAKE_PROJECT_NAME}_exe -p) +# # creat a test +# add_test(NAME ${CMAKE_PROJECT_NAME}_test COMMAND ${CMAKE_PROJECT_NAME}_exe -p) diff --git a/project/raspberrypi4b/driver/src/raspberrypi4b_driver_ws2812b_interface.c b/project/raspberrypi4b/driver/src/raspberrypi4b_driver_ws2812b_interface.c index f03acfc..d09ccdb 100644 --- a/project/raspberrypi4b/driver/src/raspberrypi4b_driver_ws2812b_interface.c +++ b/project/raspberrypi4b/driver/src/raspberrypi4b_driver_ws2812b_interface.c @@ -54,7 +54,7 @@ static int gs_fd; /**< spi handle */ * - 1 spi init 10mhz failed * @note none */ -uint8_t ws2812b_interface_spi_10mhz_init(void) +uint8_t ws2812b_interface_spi_init(void) { return spi_init(SPI_DEVICE_NAME, &gs_fd, SPI_MODE_TYPE_3, 1000 * 1000 * 10); } @@ -85,6 +85,24 @@ uint8_t ws2812b_interface_spi_write_cmd(uint8_t *buf, uint16_t len) return spi_write_cmd(gs_fd, buf, len); } +/** + * @brief WS1812B One Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_one_code(void) +{ + return 0xFFF8U; +} + +/** + * @brief WS1812B Zero Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_zero_code(void) +{ + return 0xE000U; +} + /** * @brief interface delay ms * @param[in] ms diff --git a/project/stm32f407/driver/src/stm32f407_driver_ws2812b_interface.c b/project/stm32f407/driver/src/stm32f407_driver_ws2812b_interface.c index 1463c1a..cdd6626 100644 --- a/project/stm32f407/driver/src/stm32f407_driver_ws2812b_interface.c +++ b/project/stm32f407/driver/src/stm32f407_driver_ws2812b_interface.c @@ -47,7 +47,7 @@ * - 1 spi init 10mhz failed * @note none */ -uint8_t ws2812b_interface_spi_10mhz_init(void) +uint8_t ws2812b_interface_spi_init(void) { return spi_init(SPI_MODE_3); } @@ -64,6 +64,24 @@ uint8_t ws2812b_interface_spi_deinit(void) return spi_deinit(); } +/** + * @brief WS1812B One Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_one_code(void) +{ + return 0xFF00U; +} + +/** + * @brief WS1812B Zero Code bit map + * @note Depends on SPI speed + */ +uint16_t ws2812b_interface_zero_code(void) +{ + return 0xF000U; +} + /** * @brief interface spi bus write command * @param[in] *buf points to a data buffer diff --git a/src/driver_ws2812b.c b/src/driver_ws2812b.c index 4ad3031..2a606c1 100644 --- a/src/driver_ws2812b.c +++ b/src/driver_ws2812b.c @@ -68,9 +68,9 @@ uint8_t ws2812b_init(ws2812b_handle_t *handle) { return 3; /* return error */ } - if (handle->spi_10mhz_init == NULL) /* check spi_10mhz_init */ + if (handle->spi_init == NULL) /* check spi_10mhz_init */ { - handle->debug_print("ws2812b: spi_10mhz_init is null.\n"); /* spi_10mhz_init is null */ + handle->debug_print("ws2812b: spi_init is null.\n"); /* spi_10mhz_init is null */ return 3; /* return error */ } @@ -86,6 +86,18 @@ uint8_t ws2812b_init(ws2812b_handle_t *handle) return 3; /* return error */ } + if (handle->one_code == NULL) /* check spi_deinit */ + { + handle->debug_print("ws2812b: one_code is null.\n"); /* spi_deinit is null */ + + return 3; /* return error */ + } + if (handle->zero_code == NULL) /* check spi_deinit */ + { + handle->debug_print("ws2812b: zero_code is null.\n"); /* spi_deinit is null */ + + return 3; /* return error */ + } if (handle->delay_ms == NULL) /* check delay_ms */ { handle->debug_print("ws2812b: delay_ms is null.\n"); /* delay_ms is null */ @@ -93,7 +105,7 @@ uint8_t ws2812b_init(ws2812b_handle_t *handle) return 3; /* return error */ } - if (handle->spi_10mhz_init() != 0) /* spi init */ + if (handle->spi_init() != 0) /* spi init */ { handle->debug_print("ws2812b: spi init failed.\n"); /* spi init failed */ @@ -110,13 +122,11 @@ uint8_t ws2812b_init(ws2812b_handle_t *handle) * @param[in] *temp points to a temp buffer * @note none */ -static void a_ws2812b_write_one_frame(uint32_t rgb, uint8_t temp[48]) +static void a_ws2812b_write_one_frame(uint32_t rgb, uint8_t temp[48], const uint16_t one_code, const uint16_t zero_code) { uint8_t r, g, b; uint8_t i, j; uint32_t c, point; - const uint16_t one_code = 0xFFF8U; - const uint16_t zero_code = 0xE000U; r = (uint8_t)((rgb >> 16) & 0xFF); /* set red */ g = (uint8_t)((rgb >> 8) & 0xFF); /* set green */ @@ -203,7 +213,7 @@ uint8_t ws2812b_write(ws2812b_handle_t *handle, uint32_t *rgb, uint32_t len, uin return 5; /* return error */ } - bit_size = WS2812B_RESET_BIT_FRAME_LEN; /* set the bit size */ + bit_size = bit_size = 24 * 16 * len; /* set the bit size */ bit_size = bit_size / 8; /* set the bit size */ if (bit_size > temp_len) /* check temp length */ { @@ -235,7 +245,7 @@ uint8_t ws2812b_write(ws2812b_handle_t *handle, uint32_t *rgb, uint32_t len, uin for (i = 0; i < len; i++) /* set the color frame */ { - a_ws2812b_write_one_frame(rgb[i], &temp[i * 48]); /* set color */ + a_ws2812b_write_one_frame(rgb[i], &temp[i * 48], handle->one_code(), handle->zero_code()); /* set color */ } if (handle->spi_write_cmd(temp, (uint16_t)bit_size) != 0) /* write command */ @@ -360,7 +370,7 @@ uint8_t ws2812b_write_only_color(ws2812b_handle_t *handle, uint32_t *rgb, uint32 for (i = 0; i < len; i++) /* set the color frame */ { - a_ws2812b_write_one_frame(rgb[i], &temp[i * 48]); /* set color */ + a_ws2812b_write_one_frame(rgb[i], &temp[i * 48], handle->one_code(), handle->zero_code()); /* set color */ } if (handle->spi_write_cmd(temp, (uint16_t)bit_size) != 0) /* write command */ diff --git a/src/driver_ws2812b.h b/src/driver_ws2812b.h index e1c5ca6..9dca2df 100644 --- a/src/driver_ws2812b.h +++ b/src/driver_ws2812b.h @@ -66,9 +66,11 @@ extern "C" { */ typedef struct ws2812b_handle_s { - uint8_t (*spi_10mhz_init)(void); /**< point to a spi_10mhz_init function address */ + uint8_t (*spi_init)(void); /**< point to a spi_init function address */ uint8_t (*spi_deinit)(void); /**< point to a spi_deinit function address */ uint8_t (*spi_write_cmd)(uint8_t *buf, uint16_t len); /**< point to a spi_write_cmd function address */ + uint16_t (*one_code)(void); + uint16_t (*zero_code)(void); void (*delay_ms)(uint32_t ms); /**< point to a delay_ms function address */ void (*debug_print)(const char *const fmt, ...); /**< point to a debug_print function address */ uint8_t inited; /**< inited flag */ @@ -110,12 +112,12 @@ typedef struct ws2812b_info_s #define DRIVER_WS2812B_LINK_INIT(HANDLE, STRUCTURE) memset(HANDLE, 0, sizeof(STRUCTURE)) /** - * @brief link spi_10mhz_init function + * @brief link spi_init function * @param[in] HANDLE points to a ws2812b handle structure - * @param[in] FUC points to a spi_10mhz_init function address - * @note none + * @param[in] FUC points to a spi_init function address + * @note Spi speed is limited by ardware and is related to WS1812B one and zero codes */ -#define DRIVER_WS2812B_LINK_SPI_10MHZ_INIT(HANDLE, FUC) (HANDLE)->spi_10mhz_init = FUC +#define DRIVER_WS2812B_LINK_SPI_INIT(HANDLE, FUC) (HANDLE)->spi_init = FUC /** * @brief link spi_deinit function @@ -133,6 +135,22 @@ typedef struct ws2812b_info_s */ #define DRIVER_WS2812B_LINK_SPI_WRITE_COMMAND(HANDLE, FUC) (HANDLE)->spi_write_cmd = FUC +/** + * @brief link one_code function + * @param[in] HANDLE points to a ws2812b handle structure + * @param[in] FUC points to a one_code function address + * @note Spi speed is limited by ardware and is related to WS1812B one and zero codes + */ +#define DRIVER_WS2812B_LINK_ONE_CODE(HANDLE, FUC) (HANDLE)->one_code = FUC + +/** + * @brief link zero_code function + * @param[in] HANDLE points to a ws2812b handle structure + * @param[in] FUC points to a zero_code function address + * @note Spi speed is limited by ardware and is related to WS1812B one and zero codes + */ +#define DRIVER_WS2812B_LINK_ZERO_CODE(HANDLE, FUC) (HANDLE)->zero_code = FUC + /** * @brief link delay_ms function * @param[in] HANDLE points to a ws2812b handle structure diff --git a/test/driver_ws2812b_write_test.c b/test/driver_ws2812b_write_test.c index 4eb0268..dda956d 100644 --- a/test/driver_ws2812b_write_test.c +++ b/test/driver_ws2812b_write_test.c @@ -37,9 +37,13 @@ #include "driver_ws2812b_write_test.h" #include +#define LED_COUNT 24 +#define CHANGE_SINGLE_LED 1 +#define TEMP_BUF_SIZE 1152 + static ws2812b_handle_t gs_handle; /**< ws2812b handle */ -static uint8_t gs_buffer[1024]; /**< inner temp buffer */ -static uint32_t gs_rgb[21]; /**< inner rgb buffer */ +static uint8_t gs_buffer[TEMP_BUF_SIZE]; /**< inner temp buffer */ +static uint32_t gs_rgb[LED_COUNT]; /**< inner rgb buffer */ /** * @brief write test @@ -60,7 +64,9 @@ uint8_t ws2812b_write_test(uint32_t cnt, uint32_t times) /* link interface function */ DRIVER_WS2812B_LINK_INIT(&gs_handle, ws2812b_handle_t); - DRIVER_WS2812B_LINK_SPI_10MHZ_INIT(&gs_handle, ws2812b_interface_spi_10mhz_init); + DRIVER_WS2812B_LINK_SPI_INIT(&gs_handle, ws2812b_interface_spi_init); + DRIVER_WS2812B_LINK_ONE_CODE(&gs_handle, ws2812b_interface_one_code); + DRIVER_WS2812B_LINK_ZERO_CODE(&gs_handle, ws2812b_interface_zero_code); DRIVER_WS2812B_LINK_SPI_DEINIT(&gs_handle, ws2812b_interface_spi_deinit); DRIVER_WS2812B_LINK_SPI_WRITE_COMMAND(&gs_handle, ws2812b_interface_spi_write_cmd); DRIVER_WS2812B_LINK_DELAY_MS(&gs_handle, ws2812b_interface_delay_ms); @@ -95,9 +101,9 @@ uint8_t ws2812b_write_test(uint32_t cnt, uint32_t times) return 1; } - if (cnt > 21) + if (cnt > LED_COUNT) { - ws2812b_interface_debug_print("ws2812b: cnt is over 21 and use 21.\n"); + ws2812b_interface_debug_print("ws2812b: cnt is over %d and use %d.\n", LED_COUNT, LED_COUNT); } /* start register test */ @@ -113,15 +119,20 @@ uint8_t ws2812b_write_test(uint32_t cnt, uint32_t times) } /* set number */ - num = cnt > 21 ? 21 : cnt; + num = cnt > LED_COUNT ? LED_COUNT : cnt; for (i = 0; i < times; i++) { - for (j = 0; j < num; j++) - { - gs_rgb[j] = color[i % 7]; + if (CHANGE_SINGLE_LED) { + gs_rgb[i % num] = color[i % 7]; + } else { + for (j = 0; j < num; j++) + { + gs_rgb[j] = color[i % 7]; + } } - res = ws2812b_write(&gs_handle, (uint32_t *)gs_rgb, num, gs_buffer, 1024); + + res = ws2812b_write(&gs_handle, (uint32_t *)gs_rgb, num, gs_buffer, TEMP_BUF_SIZE); if (res != 0) { ws2812b_interface_debug_print("ws2812b: write failed.\n"); @@ -131,7 +142,7 @@ uint8_t ws2812b_write_test(uint32_t cnt, uint32_t times) } /* delay 1000 ms */ - ws2812b_interface_delay_ms(1000); + ws2812b_interface_delay_ms(50); /* output */ ws2812b_interface_debug_print("ws2812b: %d/%d times.\n", i + 1, times);