mirror of
https://github.com/DarthAffe/RGB.NET-PicoPi.git
synced 2025-12-12 21:38:41 +00:00
Added firmware v1
This commit is contained in:
parent
a9e5a2062d
commit
d1f3727f31
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,3 +9,5 @@ install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
[Bb]uild/
|
||||
.vscode
|
||||
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(PICO_SDK_PATH "../../pico-sdk")
|
||||
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(RGB.NET C CXX ASM)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(RGB.NET)
|
||||
|
||||
pico_generate_pio_header(RGB.NET ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)
|
||||
|
||||
target_include_directories(RGB.NET PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_sources(RGB.NET PRIVATE RGB.NET.c usb_descriptors.c)
|
||||
|
||||
pico_set_program_name(RGB.NET "RGB.NET")
|
||||
pico_set_program_version(RGB.NET "1.0")
|
||||
|
||||
pico_enable_stdio_uart(RGB.NET 0)
|
||||
pico_enable_stdio_usb(RGB.NET 0)
|
||||
|
||||
target_link_libraries(RGB.NET PRIVATE pico_stdlib hardware_pio pico_multicore tinyusb_device tinyusb_board hardware_flash)
|
||||
|
||||
pico_add_extra_outputs(RGB.NET)
|
||||
459
RGB.NET.c
Normal file
459
RGB.NET.c
Normal file
@ -0,0 +1,459 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "hardware/flash.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/watchdog.h"
|
||||
#include "ws2812.pio.h"
|
||||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
|
||||
// This code allows to use the Rasperry Pi PICO as PicoPiDevice.
|
||||
|
||||
#define VERSION 1
|
||||
|
||||
//#### CONFIGURATION ####
|
||||
|
||||
// The default amount of LED of each channel. This can be configured afterwards through USB.
|
||||
// Setting a channel to 0 LEDs will disable it.
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_1 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_2 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_3 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_4 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_5 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_6 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_7 255
|
||||
#define DEFAULT_LED_COUNT_CHANNEL_8 255
|
||||
|
||||
#define PIN_CHANNEL_1 8
|
||||
#define PIN_CHANNEL_2 9
|
||||
#define PIN_CHANNEL_3 10
|
||||
#define PIN_CHANNEL_4 11
|
||||
#define PIN_CHANNEL_5 12
|
||||
#define PIN_CHANNEL_6 13
|
||||
#define PIN_CHANNEL_7 14
|
||||
#define PIN_CHANNEL_8 15
|
||||
|
||||
//#######################
|
||||
|
||||
#define CHANNELS 8 // Change this only if you add or remove channels in the implementation-part. To disable channels set them to 0 LED.
|
||||
#define MAX_CHANNEL_SIZE 255
|
||||
|
||||
#define BUFFER_SIZE (MAX_CHANNEL_SIZE * 3)
|
||||
#define TRANSFER_BUFFER_SIZE ((BUFFER_SIZE + 3) * CHANNELS)
|
||||
|
||||
#define OFFSET_MULTIPLIER 60
|
||||
|
||||
#define FLASH_CONFIG_OFFSET (256 * 1024)
|
||||
#define CONFIG_MAGIC_NUMBER_LENGTH 8
|
||||
|
||||
const PIO CHANNEL_PIO[CHANNELS] = {pio0, pio0, pio0, pio0, pio1, pio1, pio1, pio1};
|
||||
const uint CHANNEL_SM[CHANNELS] = {0, 1, 2, 3, 0, 1, 2, 3};
|
||||
|
||||
uint8_t pins[CHANNELS] = {PIN_CHANNEL_1, PIN_CHANNEL_2, PIN_CHANNEL_3, PIN_CHANNEL_4, PIN_CHANNEL_5, PIN_CHANNEL_6, PIN_CHANNEL_7, PIN_CHANNEL_8};
|
||||
uint8_t led_counts[CHANNELS] = {DEFAULT_LED_COUNT_CHANNEL_1, DEFAULT_LED_COUNT_CHANNEL_2, DEFAULT_LED_COUNT_CHANNEL_3, DEFAULT_LED_COUNT_CHANNEL_4, DEFAULT_LED_COUNT_CHANNEL_5, DEFAULT_LED_COUNT_CHANNEL_6, DEFAULT_LED_COUNT_CHANNEL_7, DEFAULT_LED_COUNT_CHANNEL_8};
|
||||
|
||||
uint8_t buffer_channel_1[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_2[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_3[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_4[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_5[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_6[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_7[BUFFER_SIZE];
|
||||
uint8_t buffer_channel_8[BUFFER_SIZE];
|
||||
|
||||
uint8_t *buffers[] = {(uint8_t *)buffer_channel_1, (uint8_t *)buffer_channel_2, (uint8_t *)buffer_channel_3, (uint8_t *)buffer_channel_4, (uint8_t *)buffer_channel_5, (uint8_t *)buffer_channel_6, (uint8_t *)buffer_channel_7, (uint8_t *)buffer_channel_8};
|
||||
uint8_t send_buffer[CFG_TUD_ENDPOINT0_SIZE];
|
||||
uint8_t read_buffer[CFG_TUD_ENDPOINT0_SIZE];
|
||||
uint8_t transfer_buffer[TRANSFER_BUFFER_SIZE];
|
||||
uint32_t transfer_buffer_count = 0;
|
||||
uint32_t transfer_length = 0;
|
||||
|
||||
const uint8_t config_magic_number[CONFIG_MAGIC_NUMBER_LENGTH] = {0x52, 0x47, 0x42, 0x2E, 0x4E, 0x45, 0x54, VERSION};
|
||||
const uint8_t *config = (const uint8_t *)(XIP_BASE + FLASH_CONFIG_OFFSET);
|
||||
|
||||
static inline void put_pixel(PIO pio, int sm, uint32_t pixel_grb)
|
||||
{
|
||||
pio_sm_put_blocking(pio, sm, pixel_grb << 8u);
|
||||
}
|
||||
|
||||
static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return ((uint32_t)(r) << 8) | ((uint32_t)(g) << 16) | ((uint32_t)(b));
|
||||
}
|
||||
|
||||
void init_channel(int channel)
|
||||
{
|
||||
PIO pio = CHANNEL_PIO[channel];
|
||||
uint sm = CHANNEL_SM[channel];
|
||||
uint8_t pin = pins[channel];
|
||||
|
||||
uint offset = pio_add_program(pio, &ws2812_program);
|
||||
ws2812_program_init(pio, sm, offset, pin, 800000, false);
|
||||
}
|
||||
|
||||
void update_channel(int channel)
|
||||
{
|
||||
PIO pio = CHANNEL_PIO[channel];
|
||||
uint sm = CHANNEL_SM[channel];
|
||||
uint8_t *data = buffers[channel];
|
||||
int count = led_counts[channel];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int offset = i * 3;
|
||||
uint8_t r = data[offset];
|
||||
uint8_t g = data[offset + 1];
|
||||
uint8_t b = data[offset + 2];
|
||||
uint32_t pixel = urgb_u32(r, g, b);
|
||||
put_pixel(pio, sm, pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void stage_channel_update(uint8_t channel, uint8_t const *buffer, uint16_t length)
|
||||
{
|
||||
uint8_t *data = buffers[channel];
|
||||
bool update = buffer[0] > 0;
|
||||
int offset = buffer[1] * OFFSET_MULTIPLIER;
|
||||
int dataLength = length - 2;
|
||||
int bufferSize = led_counts[channel] * 3;
|
||||
if ((offset + dataLength) > bufferSize)
|
||||
{
|
||||
dataLength = bufferSize - offset;
|
||||
if (dataLength <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
multicore_fifo_pop_blocking();
|
||||
__builtin_memcpy(data + offset, buffer + 2, dataLength);
|
||||
if (update)
|
||||
{
|
||||
multicore_fifo_push_blocking(channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
multicore_fifo_push_blocking(0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void stage_channel_update_bulk(uint8_t channel, uint8_t const *buffer, uint16_t length)
|
||||
{
|
||||
uint8_t *data = buffers[channel];
|
||||
|
||||
multicore_fifo_pop_blocking();
|
||||
__builtin_memcpy(data, buffer, length);
|
||||
multicore_fifo_push_blocking(channel);
|
||||
}
|
||||
|
||||
void send_info(uint8_t info)
|
||||
{
|
||||
__builtin_memset(send_buffer, 0, sizeof(send_buffer));
|
||||
send_buffer[0] = info;
|
||||
tud_hid_report(0, send_buffer, 64);
|
||||
}
|
||||
|
||||
void send_info_n(uint8_t const *data, uint16_t length)
|
||||
{
|
||||
__builtin_memset(send_buffer, 0, sizeof(send_buffer));
|
||||
for (uint16_t i = 0; i < length; i++)
|
||||
{
|
||||
send_buffer[i] = data[i];
|
||||
}
|
||||
tud_hid_report(0, send_buffer, 64);
|
||||
}
|
||||
|
||||
void write_configuration(uint8_t const *ledCounts, uint8_t const *pins, uint16_t length)
|
||||
{
|
||||
if (length < CHANNELS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t config_buffer[FLASH_PAGE_SIZE];
|
||||
__builtin_memset(config_buffer, 0, FLASH_PAGE_SIZE);
|
||||
__builtin_memcpy(config_buffer, config_magic_number, CONFIG_MAGIC_NUMBER_LENGTH);
|
||||
|
||||
for (int i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
uint8_t ledCount = ledCounts[i];
|
||||
if (ledCount > MAX_CHANNEL_SIZE)
|
||||
{
|
||||
ledCount = MAX_CHANNEL_SIZE;
|
||||
}
|
||||
config_buffer[CONFIG_MAGIC_NUMBER_LENGTH + i] = ledCount;
|
||||
config_buffer[CONFIG_MAGIC_NUMBER_LENGTH + CHANNELS + i] = pins[i];
|
||||
}
|
||||
|
||||
flash_range_erase(FLASH_CONFIG_OFFSET, FLASH_SECTOR_SIZE);
|
||||
flash_range_program(FLASH_CONFIG_OFFSET, config_buffer, FLASH_PAGE_SIZE);
|
||||
|
||||
watchdog_reboot(0, SRAM_END, 100);
|
||||
}
|
||||
|
||||
void load_configuration()
|
||||
{
|
||||
for (int i = 0; i < CONFIG_MAGIC_NUMBER_LENGTH; i++)
|
||||
{
|
||||
if (config[i] != config_magic_number[i])
|
||||
{
|
||||
write_configuration(led_counts, pins, CHANNELS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
led_counts[i] = config[CONFIG_MAGIC_NUMBER_LENGTH + i];
|
||||
pins[i] = config[CONFIG_MAGIC_NUMBER_LENGTH + CHANNELS + i];
|
||||
}
|
||||
}
|
||||
|
||||
void process_command(uint8_t command, uint8_t const *data, uint16_t length)
|
||||
{
|
||||
int request = command & 0x0F;
|
||||
int channel = (command >> 4) & 0x0F;
|
||||
|
||||
if (channel == 0) // Device-commands
|
||||
{
|
||||
if (request == 0x01)
|
||||
{
|
||||
send_info(CHANNELS);
|
||||
}
|
||||
else if (request == 0x0A)
|
||||
{
|
||||
write_configuration(data, pins, length);
|
||||
}
|
||||
else if (request == 0x0B)
|
||||
{
|
||||
write_configuration(led_counts, data, length);
|
||||
}
|
||||
else if (request == 0x0E)
|
||||
{
|
||||
uint8_t id[8];
|
||||
flash_get_unique_id(id);
|
||||
send_info_n(id, sizeof(id));
|
||||
}
|
||||
else if (request == 0x0F)
|
||||
{
|
||||
send_info(VERSION);
|
||||
}
|
||||
}
|
||||
else if (channel <= CHANNELS)
|
||||
{
|
||||
channel--;
|
||||
if (request == 0x01)
|
||||
{
|
||||
stage_channel_update(channel, data, length);
|
||||
}
|
||||
else if (request == 0x02)
|
||||
{
|
||||
stage_channel_update_bulk(channel, data, length);
|
||||
}
|
||||
else if (request == 0x0A)
|
||||
{
|
||||
send_info(led_counts[channel]);
|
||||
}
|
||||
else if (request == 0x0B)
|
||||
{
|
||||
send_info(pins[channel]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_transfer_buffer()
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
while (offset < transfer_buffer_count)
|
||||
{
|
||||
uint8_t command = transfer_buffer[offset++];
|
||||
uint8_t payload_length = ((uint16_t)transfer_buffer[offset++] << 8) | transfer_buffer[offset++];
|
||||
process_command(command, transfer_buffer + offset, payload_length);
|
||||
offset += payload_length;
|
||||
}
|
||||
|
||||
transfer_buffer_count = 0;
|
||||
transfer_length = 0;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (int i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
if (led_counts[i] > 0)
|
||||
{
|
||||
uint8_t *data = buffers[i];
|
||||
|
||||
multicore_fifo_pop_blocking();
|
||||
__builtin_memset(data, 0, BUFFER_SIZE);
|
||||
multicore_fifo_push_blocking(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
(void)report_id;
|
||||
(void)report_type;
|
||||
(void)buffer;
|
||||
(void)reqlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
(void)report_type;
|
||||
|
||||
process_command(buffer[0], buffer + 1, bufsize - 1);
|
||||
}
|
||||
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void tud_vendor_task(void)
|
||||
{
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
if (tud_vendor_available())
|
||||
{
|
||||
uint8_t *readBuffer = read_buffer;
|
||||
uint32_t count = tud_vendor_read(read_buffer, sizeof(read_buffer));
|
||||
while (count > 0)
|
||||
{
|
||||
if (transfer_length == 0)
|
||||
{
|
||||
if (count < 2)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
transfer_length = ((uint16_t)readBuffer[0] << 8) | readBuffer[1];
|
||||
readBuffer += 2;
|
||||
count -= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t missingData = transfer_length - transfer_buffer_count;
|
||||
uint32_t copyAmount = missingData > count ? count : missingData;
|
||||
|
||||
__builtin_memcpy(transfer_buffer + transfer_buffer_count, readBuffer, copyAmount);
|
||||
|
||||
transfer_buffer_count += copyAmount;
|
||||
missingData -= copyAmount;
|
||||
|
||||
if (missingData == 0)
|
||||
{
|
||||
process_transfer_buffer();
|
||||
}
|
||||
|
||||
readBuffer += copyAmount;
|
||||
count -= copyAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop_core1()
|
||||
{
|
||||
multicore_fifo_push_blocking(0);
|
||||
while (1)
|
||||
{
|
||||
uint32_t channel = multicore_fifo_pop_blocking();
|
||||
if (channel < CHANNELS)
|
||||
{
|
||||
update_channel(channel);
|
||||
}
|
||||
multicore_fifo_push_blocking(channel);
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
watchdog_update();
|
||||
tud_task();
|
||||
tud_vendor_task();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
load_configuration();
|
||||
|
||||
watchdog_enable(500, 1);
|
||||
|
||||
for (int i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
if (led_counts[i] > 0)
|
||||
{
|
||||
init_channel(i);
|
||||
}
|
||||
}
|
||||
|
||||
tusb_init();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
multicore_launch_core1(loop_core1);
|
||||
|
||||
while (1)
|
||||
{
|
||||
loop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Configuration Validation
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_1 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 1
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_2 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 2
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_3 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 3
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_4 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 4
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_5 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 5
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_6 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 6
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_7 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 7
|
||||
#endif
|
||||
|
||||
#if DEFAULT_LED_COUNT_CHANNEL_8 > MAX_CHANNEL_SIZE
|
||||
#error There are more than MAX_CHANNEL_SIZE LEDs in channel 8
|
||||
#endif
|
||||
|
||||
#if CHANNELS != 8
|
||||
#error Channel-count can not be changed without adapting the code for it!
|
||||
#endif
|
||||
|
||||
#if MAX_CHANNEL_SIZE != 255
|
||||
#error Max channel size can not be changed without adapting the code for it!
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_HID < 1
|
||||
#error At least one HID-endpoint is required!
|
||||
#endif
|
||||
62
pico_sdk_import.cmake
Normal file
62
pico_sdk_import.cmake
Normal file
@ -0,0 +1,62 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||
113
tusb_config.h
Normal file
113
tusb_config.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
#endif
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_DEVICE_RHPORT_NUM
|
||||
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
|
||||
#ifndef BOARD_DEVICE_RHPORT_SPEED
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
|
||||
CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56)
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
|
||||
#else
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Device mode with rhport and speed defined by board.mk
|
||||
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#else
|
||||
#error "Incorrect RHPort configuration"
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_HID 1
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 1
|
||||
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE 1024
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE 1024
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
175
usb_descriptors.c
Normal file
175
usb_descriptors.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tusb.h"
|
||||
#include "hardware/flash.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0x1209,
|
||||
.idProduct = 0x2812,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const *tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *)&desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HID Report Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE)};
|
||||
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
(void)itf;
|
||||
return desc_hid_report;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
ITF_NUM_HID,
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
ITF_NUM_DATA,
|
||||
#endif
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN + TUD_VENDOR_DESC_LEN)
|
||||
#else
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
|
||||
#endif
|
||||
|
||||
#define EPNUM_HID 0x01
|
||||
#define USBD_DATA_EP_OUT 0x02
|
||||
#define USBD_DATA_EP_IN 0x82
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 250),
|
||||
TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 1),
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_NUM_DATA, 1, USBD_DATA_EP_OUT, USBD_DATA_EP_IN, 64),
|
||||
#endif
|
||||
};
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void)index; // for multiple configurations
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
|
||||
"RGB.NET", // 1: Manufacturer
|
||||
"RGB.NET WS2812B controller", // 2: Product
|
||||
"0000000000000000", // 3: Serials, replaced with chip ID
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void)langid;
|
||||
|
||||
uint8_t chr_count;
|
||||
|
||||
char serial[16];
|
||||
uint8_t id[8];
|
||||
flash_get_unique_id(id);
|
||||
sprintf(serial, "%X", id);
|
||||
string_desc_arr[3] = serial;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
|
||||
return NULL;
|
||||
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
if (chr_count > 31)
|
||||
chr_count = 31;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for (uint8_t i = 0; i < chr_count; i++)
|
||||
{
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
38
ws2812.pio
Normal file
38
ws2812.pio
Normal file
@ -0,0 +1,38 @@
|
||||
.program ws2812
|
||||
.side_set 1
|
||||
|
||||
.define public T1 2
|
||||
.define public T2 5
|
||||
.define public T3 3
|
||||
|
||||
.wrap_target
|
||||
bitloop:
|
||||
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
|
||||
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
|
||||
do_one:
|
||||
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
|
||||
do_zero:
|
||||
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
|
||||
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
|
||||
pio_sm_config c = ws2812_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
|
||||
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
Loading…
x
Reference in New Issue
Block a user