diff options
| author | hathach <[email protected]> | 2025-07-09 13:07:13 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2025-07-09 13:07:13 +0700 |
| commit | 386f5518992166dec0830659b21663aeddae4fc8 (patch) | |
| tree | c143f921067c6fbe0bbc9d68c8864b9b1fc71d11 /examples/device | |
| parent | 965e26de1dcbd87c3b0e371857bc4ad0b9b3e840 (diff) | |
| parent | 04fb5873effebb3dff770e3257ed789ee36f3846 (diff) | |
Merge branch 'refs/heads/master' into fork/ChrisDeadman/hcd-samd-support
Diffstat (limited to 'examples/device')
129 files changed, 2306 insertions, 2983 deletions
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index 26a808a21..bb7dd0a0f 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../hw/bsp/family_support.cmake) diff --git a/examples/device/CMakePresets.json b/examples/device/CMakePresets.json new file mode 100644 index 000000000..c22e8c0ec --- /dev/null +++ b/examples/device/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_4_channel_mic/CMakeLists.txt b/examples/device/audio_4_channel_mic/CMakeLists.txt index 0f5d36193..c8086ae46 100644 --- a/examples/device/audio_4_channel_mic/CMakeLists.txt +++ b/examples/device/audio_4_channel_mic/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/audio_4_channel_mic/CMakePresets.json b/examples/device/audio_4_channel_mic/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/audio_4_channel_mic/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_4_channel_mic/src/main.c b/examples/device/audio_4_channel_mic/src/main.c index e8c40309e..9b169e77e 100644 --- a/examples/device/audio_4_channel_mic/src/main.c +++ b/examples/device/audio_4_channel_mic/src/main.c @@ -31,10 +31,10 @@ * $ python3 plot_audio_samples.py */ -#include <stdlib.h> +#include <math.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <math.h> #include "bsp/board_api.h" #include "tusb.h" @@ -43,14 +43,14 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ -#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE +#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE /* Blink pattern * - 250 ms : device not mounted * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -60,36 +60,29 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0 uint32_t sampFreq; uint8_t clkValid; // Range states -audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state -audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state +audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state +audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state -#if CFG_TUD_AUDIO_ENABLE_ENCODING -// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2 -uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; -#else // Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3 -uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000]; -#endif +uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000]; void led_blinking_task(void); void audio_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -106,47 +99,23 @@ int main(void) sampleFreqRng.subrange[0].bRes = 0; // Generate dummy data -#if CFG_TUD_AUDIO_ENABLE_ENCODING - uint16_t * p_buff = i2s_dummy_buffer[0]; + uint16_t *p_buff = i2s_dummy_buffer; uint16_t dataVal = 0; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { + for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) { // CH0 saw wave *p_buff++ = dataVal; // CH1 inverted saw wave - *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; - dataVal+= 32; - } - p_buff = i2s_dummy_buffer[1]; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { + *p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal; + dataVal += 32; // CH3 square wave - *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; + *p_buff++ = cnt < (AUDIO_SAMPLE_RATE / 1000 / 2) ? 3400 : 5000; // CH4 sinus wave - float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); - *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); + float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000); + *p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000); } -#else - uint16_t * p_buff = i2s_dummy_buffer; - uint16_t dataVal = 0; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { - // CH0 saw wave - *p_buff++ = dataVal; - // CH1 inverted saw wave - *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; - dataVal+= 32; - // CH3 square wave - *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; - // CH4 sinus wave - float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); - *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); - } -#endif - while (1) - { - tud_task(); // tinyusb device task + while (1) { + tud_task();// tinyusb device task led_blinking_task(); audio_task(); } @@ -157,29 +126,25 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } @@ -187,23 +152,15 @@ void tud_resume_cb(void) // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ - // Yet to be filled - e.g. read audio from I2S buffer. - // Here we simulate a I2S receive callback every 1ms. +// This task simulates an audio receive callback, one frame is received every 1ms. +// We assume that the audio data is read from an I2S buffer. +// In a real application, this would be replaced with actual I2S receive callback. +void audio_task(void) { static uint32_t start_ms = 0; uint32_t curr_ms = board_millis(); - if ( start_ms == curr_ms ) return; // not enough time + if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; -#if CFG_TUD_AUDIO_ENABLE_ENCODING - // Write I2S buffer into FIFO - for (uint8_t cnt=0; cnt < 2; cnt++) - { - tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX); - } -#else - tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX); -#endif + tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX); } //--------------------------------------------------------------------+ @@ -211,8 +168,7 @@ void audio_task(void) //--------------------------------------------------------------------+ // Invoked when audio class specific set request received for an EP -bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -224,14 +180,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an interface -bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -243,14 +200,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; // Page 91 in UAC2 specification @@ -265,40 +223,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); // If request is for our feature unit - if ( entityID == 2 ) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Request uses format layout 1 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); - mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; + mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur; TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); - return true; + return true; case AUDIO_FU_CTRL_VOLUME: // Request uses format layout 2 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); - volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur; + volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur; TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); - return true; + return true; // Unknown/Unsupported control default: TU_BREAKPOINT(); - return false; + return false; } } - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an EP -bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -306,16 +261,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; // return tud_control_xfer(rhport, p_request, &tmp, 1); - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an interface -bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -323,14 +279,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -340,12 +297,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * uint8_t entityID = TU_U16_HIGH(p_request->wIndex); // Input terminal (Microphone input) - if (entityID == 1) - { - switch ( ctrlSel ) - { - case AUDIO_TE_CTRL_CONNECTOR: - { + if (entityID == 1) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { // The terminal connector control only has a get request with only the CUR attribute. audio_desc_channel_cluster_t ret; @@ -356,9 +310,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_LOG2(" Get terminal connector\r\n"); - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); - } - break; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); + } break; // Unknown/Unsupported control selector default: @@ -368,10 +321,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Feature unit - if (entityID == 2) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Audio control mute cur parameter block consists of only one byte - we thus can send it right away // There does not exist a range parameter block for mute @@ -379,8 +330,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); case AUDIO_FU_CTRL_VOLUME: - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); @@ -390,21 +340,21 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * // Copy values - only for testing - better is version below audio_control_range_2_n_t(1) - ret; + ret; ret.wNumSubRanges = 1; - ret.subrange[0].bMin = -90; // -90 dB - ret.subrange[0].bMax = 90; // +90 dB - ret.subrange[0].bRes = 1; // 1 dB steps + ret.subrange[0].bMin = -90;// -90 dB + ret.subrange[0].bMax = 90; // +90 dB + ret.subrange[0].bRes = 1; // 1 dB steps - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; // Unknown/Unsupported control default: @@ -414,14 +364,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Clock Source unit - if ( entityID == 4 ) - { - switch ( ctrlSel ) - { + if (entityID == 4) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: // channelNum is always zero in this case - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Sample Freq.\r\n"); // Buffered control transfer is needed for IN flow control to work @@ -431,12 +378,12 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_LOG2(" Get Sample Freq. range\r\n"); return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); - // Unknown/Unsupported control + // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; case AUDIO_CS_CTRL_CLK_VALID: // Only cur attribute exists for this request @@ -451,59 +398,20 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } TU_LOG2(" Unsupported entity: %d\r\n", entityID); - return false; // Yet not implemented -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - - // In read world application data flow is driven by I2S clock, - // both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used. - // For example in your I2S receive callback: - // void I2S_Rx_Callback(int channel, const void* data, uint16_t samples) - // { - // tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO); - // } - - return true; -} - -bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) n_bytes_copied; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - return true; -} - -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void) rhport; - (void) p_request; - - return true; + return false;// Yet not implemented } //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (board_millis() - start_ms < blink_interval_ms) return;// not enough time start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state;// toggle } diff --git a/examples/device/audio_4_channel_mic/src/tusb_config.h b/examples/device/audio_4_channel_mic/src/tusb_config.h index 46484f847..446a7a32a 100644 --- a/examples/device/audio_4_channel_mic/src/tusb_config.h +++ b/examples/device/audio_4_channel_mic/src/tusb_config.h @@ -115,26 +115,11 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup #define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_ENABLE_ENCODING 1 #define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1 -#if CFG_TUD_AUDIO_ENABLE_ENCODING - -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN - -#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1 -#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value -#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX) -#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device - -#else - #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device -#endif - #ifdef __cplusplus } #endif diff --git a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt index e8ca3751d..c50d4fef7 100644 --- a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt +++ b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -19,7 +19,6 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) diff --git a/examples/device/audio_4_channel_mic_freertos/CMakePresets.json b/examples/device/audio_4_channel_mic_freertos/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/audio_4_channel_mic_freertos/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_4_channel_mic_freertos/Makefile b/examples/device/audio_4_channel_mic_freertos/Makefile index df2cdef4e..bd625b345 100644 --- a/examples/device/audio_4_channel_mic_freertos/Makefile +++ b/examples/device/audio_4_channel_mic_freertos/Makefile @@ -1,38 +1,15 @@ +RTOS = freertos include ../../build_system/make/make.mk -FREERTOS_SRC = lib/FreeRTOS-Kernel -FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC) - INC += \ src \ - src/FreeRTOSConfig \ $(TOP)/hw \ - $(TOP)/$(FREERTOS_SRC)/include \ - $(TOP)/$(FREERTOS_PORTABLE_SRC) \ # Example source EXAMPLE_SOURCE = \ - src/freertos_hook.c \ src/main.c \ src/usb_descriptors.c SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) -# FreeRTOS source, all files in port folder -SRC_C += \ - $(FREERTOS_SRC)/list.c \ - $(FREERTOS_SRC)/queue.c \ - $(FREERTOS_SRC)/tasks.c \ - $(FREERTOS_SRC)/timers.c \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c)) - -SRC_S += \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s)) - -# Suppress FreeRTOS warnings -CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls - -# FreeRTOS (lto + Os) linker issue -LDFLAGS += -Wl,--undefined=vTaskSwitchContext - include ../../build_system/make/rules.mk diff --git a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h deleted file mode 100644 index 869500ad2..000000000 --- a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FreeRTOS Kernel V10.0.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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. If you wish to use our Amazon - * FreeRTOS name, please do so in a fair use way that does not cause confusion. - * - * 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. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -// skip if included from IAR assembler -#ifndef __IASMARM__ - -// Include MCU header -#include "bsp/board_mcu.h" - -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 - #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" -#endif - -// TODO fix later -#if CFG_TUSB_MCU == OPT_MCU_MM32F327X - extern u32 SystemCoreClock; -#else - // FIXME cause redundant-decls warnings - extern uint32_t SystemCoreClock; -#endif - -#endif - -/* Cortex M23/M33 port configuration. */ -#define configENABLE_MPU 0 -#define configENABLE_FPU 1 -#define configENABLE_TRUSTZONE 0 -#define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) -#define configRUN_FREERTOS_SECURE_ONLY 1 - -#define configUSE_PREEMPTION 1 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configCPU_CLOCK_HZ SystemCoreClock -#define configTICK_RATE_HZ ( 1000 ) -#define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) -#define configMAX_TASK_NAME_LEN 16 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configQUEUE_REGISTRY_SIZE 4 -#define configUSE_QUEUE_SETS 0 -#define configUSE_TIME_SLICING 0 -#define configUSE_NEWLIB_REENTRANT 0 -#define configENABLE_BACKWARD_COMPATIBILITY 1 -#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 - -#define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 0 - -/* Hook function related definitions. */ -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning -#define configCHECK_FOR_STACK_OVERFLOW 2 -#define configCHECK_HANDLER_INSTALLATION 0 - -/* Run time and task stats gathering related definitions. */ -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 1 // legacy trace -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES 2 - -/* Software timer related definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) -#define configTIMER_QUEUE_LENGTH 32 -#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE - -/* Optional functions - most linkers will remove unused functions anyway. */ -#define INCLUDE_vTaskPrioritySet 0 -#define INCLUDE_uxTaskPriorityGet 0 -#define INCLUDE_vTaskDelete 0 -#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY -#define INCLUDE_xResumeFromISR 0 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 0 -#define INCLUDE_xTaskGetCurrentTaskHandle 0 -#define INCLUDE_uxTaskGetStackHighWaterMark 0 -#define INCLUDE_xTaskGetIdleTaskHandle 0 -#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 -#define INCLUDE_pcTaskGetTaskName 0 -#define INCLUDE_eTaskGetState 0 -#define INCLUDE_xEventGroupSetBitFromISR 0 -#define INCLUDE_xTimerPendFunctionCall 0 - -#ifdef __RX__ -/* Renesas RX series */ -#define vSoftwareInterruptISR INT_Excep_ICU_SWINT -#define vTickISR INT_Excep_CMT0_CMI0 -#define configPERIPHERAL_CLOCK_HZ (configCPU_CLOCK_HZ/2) -#define configKERNEL_INTERRUPT_PRIORITY 1 -#define configMAX_SYSCALL_INTERRUPT_PRIORITY 4 - -#else - -/* FreeRTOS hooks to NVIC vectors */ -#define xPortPendSVHandler PendSV_Handler -#define xPortSysTickHandler SysTick_Handler -#define vPortSVCHandler SVC_Handler - -//--------------------------------------------------------------------+ -// Interrupt nesting behavior configuration. -//--------------------------------------------------------------------+ -#if defined(__NVIC_PRIO_BITS) - // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h - #define configPRIO_BITS __NVIC_PRIO_BITS - -#elif defined(__ECLIC_INTCTLBITS) - // RISC-V Bumblebee core from nuclei - #define configPRIO_BITS __ECLIC_INTCTLBITS - -#elif defined(__IASMARM__) - // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS. - // Therefore we will hard coded it to minimum value of 2 to get pass ci build. - // IAR user must update this to correct value of the target MCU - #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU" - #define configPRIO_BITS 2 - -#else - #error "FreeRTOS configPRIO_BITS to be defined" -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<<configPRIO_BITS) - 1) - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -#endif - -#endif /* __FREERTOS_CONFIG__H */ diff --git a/examples/device/audio_4_channel_mic_freertos/src/freertos_hook.c b/examples/device/audio_4_channel_mic_freertos/src/freertos_hook.c deleted file mode 100644 index 4920e3fae..000000000 --- a/examples/device/audio_4_channel_mic_freertos/src/freertos_hook.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 -//--------------------------------------------------------------------+ -#include "FreeRTOS.h" -#include "task.h" -#include "common/tusb_common.h" - - -void vApplicationMallocFailedHook(void) -{ - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName) -{ - (void) pxTask; - (void) pcTaskName; - - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an - * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is - * used by the Idle task. */ -void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) -{ - /* If the buffers to be provided to the Idle task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xIdleTaskTCB; - static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Idle task's - state will be stored. */ - *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; - - /* Pass out the array that will be used as the Idle task's stack. */ - *ppxIdleTaskStackBuffer = uxIdleTaskStack; - - /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configMINIMAL_STACK_SIZE is specified in words, not bytes. */ - *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; -} - -/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the - * application must provide an implementation of vApplicationGetTimerTaskMemory() - * to provide the memory that is used by the Timer service task. */ -void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) -{ - /* If the buffers to be provided to the Timer task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xTimerTaskTCB; - static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Timer - task's state will be stored. */ - *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; - - /* Pass out the array that will be used as the Timer task's stack. */ - *ppxTimerTaskStackBuffer = uxTimerTaskStack; - - /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ - *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; -} - -#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X -#include "iodefine.h" -void vApplicationSetupTimerInterrupt(void) -{ - /* Enable CMT0 */ - unsigned short oldPRCR = SYSTEM.PRCR.WORD; - SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1); - MSTP(CMT0) = 0; - SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR; - - CMT0.CMCNT = 0; - CMT0.CMCOR = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128); - CMT0.CMCR.WORD = TU_BIT(6) | 2; - IR(CMT0, CMI0) = 0; - IPR(CMT0, CMI0) = configKERNEL_INTERRUPT_PRIORITY; - IEN(CMT0, CMI0) = 1; - CMT.CMSTR0.BIT.STR0 = 1; -} -#endif diff --git a/examples/device/audio_4_channel_mic_freertos/src/main.c b/examples/device/audio_4_channel_mic_freertos/src/main.c index 2ac7516cd..9c76380a2 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/main.c +++ b/examples/device/audio_4_channel_mic_freertos/src/main.c @@ -31,50 +31,50 @@ * $ python3 plot_audio_samples.py */ -#include <stdlib.h> +#include <math.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <math.h> #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" - #include "freertos/semphr.h" #include "freertos/queue.h" + #include "freertos/semphr.h" #include "freertos/task.h" #include "freertos/timers.h" - #define USBD_STACK_SIZE 4096 + #define USBD_STACK_SIZE 4096 #else #include "FreeRTOS.h" - #include "semphr.h" #include "queue.h" + #include "semphr.h" #include "task.h" #include "timers.h" // Increase stack size when debug log is enabled - #define USBD_STACK_SIZE (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) + #define USBD_STACK_SIZE (4 * configMINIMAL_STACK_SIZE / 2) * (CFG_TUSB_DEBUG ? 2 : 1) #endif -#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE -#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE +#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE +#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ -#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE +#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE /* Blink pattern * - 250 ms : device not mounted * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -82,13 +82,13 @@ enum { // static task #if configSUPPORT_STATIC_ALLOCATION -StackType_t blinky_stack[BLINKY_STACK_SIZE]; +StackType_t blinky_stack[BLINKY_STACK_SIZE]; StaticTask_t blinky_taskdef; -StackType_t usb_device_stack[USBD_STACK_SIZE]; +StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; -StackType_t audio_stack[AUDIO_STACK_SIZE]; +StackType_t audio_stack[AUDIO_STACK_SIZE]; StaticTask_t audio_taskdef; #endif @@ -96,30 +96,24 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0 uint32_t sampFreq; uint8_t clkValid; // Range states -audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state -audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state +audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state +audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state -#if CFG_TUD_AUDIO_ENABLE_ENCODING -// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2 -uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; -#else // Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3 -uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000]; -#endif +uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000]; -void led_blinking_task(void* param); -void usb_device_task(void* param); -void audio_task(void* param); +void led_blinking_task(void *param); +void usb_device_task(void *param); +void audio_isr_task(void *param); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // Init values @@ -132,87 +126,62 @@ int main(void) sampleFreqRng.subrange[0].bRes = 0; // Generate dummy data -#if CFG_TUD_AUDIO_ENABLE_ENCODING - uint16_t * p_buff = i2s_dummy_buffer[0]; + uint16_t *p_buff = i2s_dummy_buffer; uint16_t dataVal = 0; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { + for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) { // CH0 saw wave *p_buff++ = dataVal; // CH1 inverted saw wave - *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; - dataVal+= 32; - } - p_buff = i2s_dummy_buffer[1]; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { + *p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal; + dataVal += 32; // CH3 square wave - *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; + *p_buff++ = cnt < (AUDIO_SAMPLE_RATE / 1000 / 2) ? 3400 : 5000; // CH4 sinus wave - float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); - *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); + float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000); + *p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000); } -#else - uint16_t * p_buff = i2s_dummy_buffer; - uint16_t dataVal = 0; - for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++) - { - // CH0 saw wave - *p_buff++ = dataVal; - // CH1 inverted saw wave - *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal; - dataVal+= 32; - // CH3 square wave - *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000; - // CH4 sinus wave - float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000); - *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000); - } -#endif #if configSUPPORT_STATIC_ALLOCATION // blinky task xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); // Create a task for tinyusb device stack - xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, usb_device_stack, &usb_device_taskdef); - // Create a task for audio - xTaskCreateStatic(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES-1, audio_stack, &audio_taskdef); + // Audio receive (I2S) ISR simulation + // To simulate a ISR the priority is set to the highest + xTaskCreateStatic(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, audio_stack, &audio_taskdef); #else xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); - xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); - xTaskCreate(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL); + xTaskCreate(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); #endif - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // only start scheduler for non-espressif mcu + #ifndef ESP_PLATFORM vTaskStartScheduler(); #endif return 0; } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) -void app_main(void) -{ +#ifdef ESP_PLATFORM +void app_main(void) { main(); } #endif // USB Device Driver task // This top level thread process all usb events and invoke callbacks -void usb_device_task(void* param) -{ +void usb_device_task(void *param) { (void) param; // init device stack on configured roothub port // This should be called after scheduler/kernel is started. // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -220,8 +189,7 @@ void usb_device_task(void* param) } // RTOS forever loop - while (1) - { + while (1) { // tinyusb device task tud_task(); } @@ -232,29 +200,25 @@ void usb_device_task(void* param) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } @@ -262,22 +226,14 @@ void tud_resume_cb(void) // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void* param) -{ +// This task simulates an audio receive ISR, one frame is received every 1ms. +// We assume that the audio data is read from an I2S buffer. +// In a real application, this would be replaced with actual I2S receive callback. +void audio_isr_task(void *param) { (void) param; - // Yet to be filled - e.g. read audio from I2S buffer. - // Here we simulate a I2S receive callback every 1ms. while (1) { vTaskDelay(1); -#if CFG_TUD_AUDIO_ENABLE_ENCODING - // Write I2S buffer into FIFO - for (uint8_t cnt=0; cnt < 2; cnt++) - { - tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX); - } -#else - tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX); -#endif + tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX); } } @@ -286,8 +242,7 @@ void audio_task(void* param) //--------------------------------------------------------------------+ // Invoked when audio class specific set request received for an EP -bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -299,14 +254,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an interface -bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -318,14 +274,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; // Page 91 in UAC2 specification @@ -340,40 +297,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); // If request is for our feature unit - if ( entityID == 2 ) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Request uses format layout 1 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); - mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; + mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur; - TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); - return true; + TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); + return true; case AUDIO_FU_CTRL_VOLUME: // Request uses format layout 2 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); - volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur; - - TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); - return true; + volume[channelNum] = ((audio_control_cur_2_t *) pBuff)->bCur; + TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); + return true; // Unknown/Unsupported control default: TU_BREAKPOINT(); - return false; + return false; } } - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an EP -bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -381,14 +334,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an interface -bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -396,14 +350,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -413,12 +368,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * uint8_t entityID = TU_U16_HIGH(p_request->wIndex); // Input terminal (Microphone input) - if (entityID == 1) - { - switch ( ctrlSel ) - { - case AUDIO_TE_CTRL_CONNECTOR: - { + if (entityID == 1) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { // The terminal connector control only has a get request with only the CUR attribute. audio_desc_channel_cluster_t ret; @@ -427,11 +379,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * ret.bmChannelConfig = 0; ret.iChannelNames = 0; - TU_LOG2(" Get terminal connector\r\n"); + TU_LOG1(" Get terminal connector\r\n"); - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); - } - break; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); + } break; // Unknown/Unsupported control selector default: @@ -441,43 +392,39 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Feature unit - if (entityID == 2) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Audio control mute cur parameter block consists of only one byte - we thus can send it right away // There does not exist a range parameter block for mute - TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); + TU_LOG1(" Get Mute of channel: %u\r\n", channelNum); return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); case AUDIO_FU_CTRL_VOLUME: - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: - TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); + TU_LOG1(" Get Volume of channel: %u\r\n", channelNum); return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); case AUDIO_CS_REQ_RANGE: - TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); + TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum); // Copy values - only for testing - better is version below - audio_control_range_2_n_t(1) - ret; + audio_control_range_2_n_t(1) ret; ret.wNumSubRanges = 1; - ret.subrange[0].bMin = -90; // -90 dB - ret.subrange[0].bMax = 90; // +90 dB - ret.subrange[0].bRes = 1; // 1 dB steps + ret.subrange[0].bMin = -90;// -90 dB + ret.subrange[0].bMax = 90; // +90 dB + ret.subrange[0].bRes = 1; // 1 dB steps - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; // Unknown/Unsupported control default: @@ -487,33 +434,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Clock Source unit - if ( entityID == 4 ) - { - switch ( ctrlSel ) - { + if (entityID == 4) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: // channelNum is always zero in this case - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: - TU_LOG2(" Get Sample Freq.\r\n"); + TU_LOG1(" Get Sample Freq.\r\n"); // Buffered control transfer is needed for IN flow control to work return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); case AUDIO_CS_REQ_RANGE: - TU_LOG2(" Get Sample Freq. range\r\n"); + TU_LOG1(" Get Sample Freq. range\r\n"); return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); - // Unknown/Unsupported control + // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; case AUDIO_CS_CTRL_CLK_VALID: // Only cur attribute exists for this request - TU_LOG2(" Get Sample Freq. valid\r\n"); + TU_LOG1(" Get Sample Freq. valid\r\n"); return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); // Unknown/Unsupported control @@ -523,52 +467,14 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } } - TU_LOG2(" Unsupported entity: %d\r\n", entityID); - return false; // Yet not implemented -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - - // In read world application data flow is driven by I2S clock, - // both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used. - // For example in your I2S receive callback: - // void I2S_Rx_Callback(int channel, const void* data, uint16_t samples) - // { - // tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO); - // } - - return true; -} - -bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) n_bytes_copied; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - return true; -} - -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void) rhport; - (void) p_request; - - return true; + TU_LOG1(" Unsupported entity: %d\r\n", entityID); + return false;// Yet not implemented } ///--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void* param) { +void led_blinking_task(void *param) { (void) param; static uint32_t start_ms = 0; static bool led_state = false; @@ -579,6 +485,6 @@ void led_blinking_task(void* param) { start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state;// toggle } } diff --git a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h index 88f20278b..f7c0efe08 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h +++ b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ extern "C" { #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif @@ -121,26 +121,11 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup #define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_ENABLE_ENCODING 1 #define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1 -#if CFG_TUD_AUDIO_ENABLE_ENCODING - -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN - -#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1 -#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value -#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX) -#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device - -#else - #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device -#endif - #ifdef __cplusplus } #endif diff --git a/examples/device/audio_test/CMakeLists.txt b/examples/device/audio_test/CMakeLists.txt index f61e1b640..6a7e68c3d 100644 --- a/examples/device/audio_test/CMakeLists.txt +++ b/examples/device/audio_test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/audio_test/CMakePresets.json b/examples/device/audio_test/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/audio_test/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_test/src/main.c b/examples/device/audio_test/src/main.c index 018c48994..07728e24a 100644 --- a/examples/device/audio_test/src/main.c +++ b/examples/device/audio_test/src/main.c @@ -31,8 +31,8 @@ * $ python3 plot_audio_samples.py */ -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "bsp/board_api.h" @@ -47,7 +47,7 @@ * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -57,32 +57,30 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0 uint32_t sampFreq; uint8_t clkValid; // Range states -audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state -audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state +audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state +audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state // Audio test data -uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2]; +uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2]; uint16_t startVal = 0; void led_blinking_task(void); void audio_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -98,9 +96,8 @@ int main(void) sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE; sampleFreqRng.subrange[0].bRes = 0; - while (1) - { - tud_task(); // tinyusb device task + while (1) { + tud_task();// tinyusb device task led_blinking_task(); audio_task(); } @@ -111,40 +108,45 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } + //--------------------------------------------------------------------+ // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ - // Yet to be filled - e.g. put meas data into TX FIFOs etc. - // asm("nop"); +// This task simulates an audio receive callback, one frame is received every 1ms. +// We assume that the audio data is read from an I2S buffer. +// In a real application, this would be replaced with actual I2S receive callback. +void audio_task(void) { + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if (start_ms == curr_ms) return;// not enough time + start_ms = curr_ms; + for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) { + test_buffer_audio[cnt] = startVal++; + } + tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio)); } //--------------------------------------------------------------------+ @@ -152,8 +154,7 @@ void audio_task(void) //--------------------------------------------------------------------+ // Invoked when audio class specific set request received for an EP -bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -165,14 +166,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an interface -bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -184,14 +186,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; // Page 91 in UAC2 specification @@ -206,40 +209,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); // If request is for our feature unit - if ( entityID == 2 ) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Request uses format layout 1 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); - mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; + mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur; TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); - return true; + return true; case AUDIO_FU_CTRL_VOLUME: // Request uses format layout 2 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); - volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur; + volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur; TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); - return true; + return true; // Unknown/Unsupported control default: TU_BREAKPOINT(); - return false; + return false; } } - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an EP -bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -247,16 +247,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; // return tud_control_xfer(rhport, p_request, &tmp, 1); - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an interface -bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -264,14 +265,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -281,12 +283,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * uint8_t entityID = TU_U16_HIGH(p_request->wIndex); // Input terminal (Microphone input) - if (entityID == 1) - { - switch ( ctrlSel ) - { - case AUDIO_TE_CTRL_CONNECTOR: - { + if (entityID == 1) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { // The terminal connector control only has a get request with only the CUR attribute. audio_desc_channel_cluster_t ret; @@ -297,9 +296,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_LOG2(" Get terminal connector\r\n"); - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); - } - break; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); + } break; // Unknown/Unsupported control selector default: @@ -309,43 +307,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Feature unit - if (entityID == 2) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Audio control mute cur parameter block consists of only one byte - we thus can send it right away // There does not exist a range parameter block for mute TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1); case AUDIO_FU_CTRL_VOLUME: - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); case AUDIO_CS_REQ_RANGE: TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); // Copy values - only for testing - better is version below audio_control_range_2_n_t(1) - ret; + ret; ret.wNumSubRanges = 1; - ret.subrange[0].bMin = -90; // -90 dB - ret.subrange[0].bMax = 90; // +90 dB - ret.subrange[0].bRes = 1; // 1 dB steps + ret.subrange[0].bMin = -90;// -90 dB + ret.subrange[0].bMax = 90; // +90 dB + ret.subrange[0].bRes = 1; // 1 dB steps - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; // Unknown/Unsupported control default: @@ -355,33 +350,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Clock Source unit - if ( entityID == 4 ) - { - switch ( ctrlSel ) - { + if (entityID == 4) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: // channelNum is always zero in this case - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Sample Freq.\r\n"); - return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); case AUDIO_CS_REQ_RANGE: TU_LOG2(" Get Sample Freq. range\r\n"); - return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); - // Unknown/Unsupported control + // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; case AUDIO_CS_CTRL_CLK_VALID: // Only cur attribute exists for this request TU_LOG2(" Get Sample Freq. valid\r\n"); - return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); // Unknown/Unsupported control default: @@ -391,39 +383,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } TU_LOG2(" Unsupported entity: %d\r\n", entityID); - return false; // Yet not implemented -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2); - - return true; -} - -bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) n_bytes_copied; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++) - { - test_buffer_audio[cnt] = startVal++; - } - - return true; + return false;// Yet not implemented } -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; (void) p_request; startVal = 0; @@ -434,15 +397,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (board_millis() - start_ms < blink_interval_ms) return;// not enough time start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state;// toggle } diff --git a/examples/device/audio_test/src/plot_audio_samples.py b/examples/device/audio_test/src/plot_audio_samples.py index ea6aa661e..2be8948ea 100755 --- a/examples/device/audio_test/src/plot_audio_samples.py +++ b/examples/device/audio_test/src/plot_audio_samples.py @@ -12,11 +12,11 @@ if __name__ == '__main__': # print(sd.query_devices()) fs = 48000 # Sample rate - duration = 100e-3 # Duration of recording + duration = 3 # Duration of recording if platform.system() == 'Windows': # MME is needed since there are more than one MicNode device APIs (at least in Windows) - device = 'Microphone (MicNode) MME' + device = 'Microphone (MicNode), Windows WASAPI' elif platform.system() == 'Darwin': device = 'MicNode' else: diff --git a/examples/device/audio_test/src/tusb_config.h b/examples/device/audio_test/src/tusb_config.h index 8c021e23c..b38958d7c 100644 --- a/examples/device/audio_test/src/tusb_config.h +++ b/examples/device/audio_test/src/tusb_config.h @@ -117,7 +117,7 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor! #define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device #ifdef __cplusplus } diff --git a/examples/device/audio_test_freertos/CMakeLists.txt b/examples/device/audio_test_freertos/CMakeLists.txt index eb22014fb..6ce9e72fe 100644 --- a/examples/device/audio_test_freertos/CMakeLists.txt +++ b/examples/device/audio_test_freertos/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -19,7 +19,6 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) diff --git a/examples/device/audio_test_freertos/CMakePresets.json b/examples/device/audio_test_freertos/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/audio_test_freertos/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_test_freertos/Makefile b/examples/device/audio_test_freertos/Makefile index df2cdef4e..bd625b345 100644 --- a/examples/device/audio_test_freertos/Makefile +++ b/examples/device/audio_test_freertos/Makefile @@ -1,38 +1,15 @@ +RTOS = freertos include ../../build_system/make/make.mk -FREERTOS_SRC = lib/FreeRTOS-Kernel -FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC) - INC += \ src \ - src/FreeRTOSConfig \ $(TOP)/hw \ - $(TOP)/$(FREERTOS_SRC)/include \ - $(TOP)/$(FREERTOS_PORTABLE_SRC) \ # Example source EXAMPLE_SOURCE = \ - src/freertos_hook.c \ src/main.c \ src/usb_descriptors.c SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) -# FreeRTOS source, all files in port folder -SRC_C += \ - $(FREERTOS_SRC)/list.c \ - $(FREERTOS_SRC)/queue.c \ - $(FREERTOS_SRC)/tasks.c \ - $(FREERTOS_SRC)/timers.c \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c)) - -SRC_S += \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s)) - -# Suppress FreeRTOS warnings -CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls - -# FreeRTOS (lto + Os) linker issue -LDFLAGS += -Wl,--undefined=vTaskSwitchContext - include ../../build_system/make/rules.mk diff --git a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h deleted file mode 100644 index 869500ad2..000000000 --- a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FreeRTOS Kernel V10.0.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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. If you wish to use our Amazon - * FreeRTOS name, please do so in a fair use way that does not cause confusion. - * - * 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. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -// skip if included from IAR assembler -#ifndef __IASMARM__ - -// Include MCU header -#include "bsp/board_mcu.h" - -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 - #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" -#endif - -// TODO fix later -#if CFG_TUSB_MCU == OPT_MCU_MM32F327X - extern u32 SystemCoreClock; -#else - // FIXME cause redundant-decls warnings - extern uint32_t SystemCoreClock; -#endif - -#endif - -/* Cortex M23/M33 port configuration. */ -#define configENABLE_MPU 0 -#define configENABLE_FPU 1 -#define configENABLE_TRUSTZONE 0 -#define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) -#define configRUN_FREERTOS_SECURE_ONLY 1 - -#define configUSE_PREEMPTION 1 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configCPU_CLOCK_HZ SystemCoreClock -#define configTICK_RATE_HZ ( 1000 ) -#define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) -#define configMAX_TASK_NAME_LEN 16 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configQUEUE_REGISTRY_SIZE 4 -#define configUSE_QUEUE_SETS 0 -#define configUSE_TIME_SLICING 0 -#define configUSE_NEWLIB_REENTRANT 0 -#define configENABLE_BACKWARD_COMPATIBILITY 1 -#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 - -#define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 0 - -/* Hook function related definitions. */ -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning -#define configCHECK_FOR_STACK_OVERFLOW 2 -#define configCHECK_HANDLER_INSTALLATION 0 - -/* Run time and task stats gathering related definitions. */ -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 1 // legacy trace -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES 2 - -/* Software timer related definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) -#define configTIMER_QUEUE_LENGTH 32 -#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE - -/* Optional functions - most linkers will remove unused functions anyway. */ -#define INCLUDE_vTaskPrioritySet 0 -#define INCLUDE_uxTaskPriorityGet 0 -#define INCLUDE_vTaskDelete 0 -#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY -#define INCLUDE_xResumeFromISR 0 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 0 -#define INCLUDE_xTaskGetCurrentTaskHandle 0 -#define INCLUDE_uxTaskGetStackHighWaterMark 0 -#define INCLUDE_xTaskGetIdleTaskHandle 0 -#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 -#define INCLUDE_pcTaskGetTaskName 0 -#define INCLUDE_eTaskGetState 0 -#define INCLUDE_xEventGroupSetBitFromISR 0 -#define INCLUDE_xTimerPendFunctionCall 0 - -#ifdef __RX__ -/* Renesas RX series */ -#define vSoftwareInterruptISR INT_Excep_ICU_SWINT -#define vTickISR INT_Excep_CMT0_CMI0 -#define configPERIPHERAL_CLOCK_HZ (configCPU_CLOCK_HZ/2) -#define configKERNEL_INTERRUPT_PRIORITY 1 -#define configMAX_SYSCALL_INTERRUPT_PRIORITY 4 - -#else - -/* FreeRTOS hooks to NVIC vectors */ -#define xPortPendSVHandler PendSV_Handler -#define xPortSysTickHandler SysTick_Handler -#define vPortSVCHandler SVC_Handler - -//--------------------------------------------------------------------+ -// Interrupt nesting behavior configuration. -//--------------------------------------------------------------------+ -#if defined(__NVIC_PRIO_BITS) - // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h - #define configPRIO_BITS __NVIC_PRIO_BITS - -#elif defined(__ECLIC_INTCTLBITS) - // RISC-V Bumblebee core from nuclei - #define configPRIO_BITS __ECLIC_INTCTLBITS - -#elif defined(__IASMARM__) - // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS. - // Therefore we will hard coded it to minimum value of 2 to get pass ci build. - // IAR user must update this to correct value of the target MCU - #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU" - #define configPRIO_BITS 2 - -#else - #error "FreeRTOS configPRIO_BITS to be defined" -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<<configPRIO_BITS) - 1) - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -#endif - -#endif /* __FREERTOS_CONFIG__H */ diff --git a/examples/device/audio_test_freertos/src/freertos_hook.c b/examples/device/audio_test_freertos/src/freertos_hook.c deleted file mode 100644 index 4920e3fae..000000000 --- a/examples/device/audio_test_freertos/src/freertos_hook.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 -//--------------------------------------------------------------------+ -#include "FreeRTOS.h" -#include "task.h" -#include "common/tusb_common.h" - - -void vApplicationMallocFailedHook(void) -{ - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName) -{ - (void) pxTask; - (void) pcTaskName; - - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an - * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is - * used by the Idle task. */ -void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) -{ - /* If the buffers to be provided to the Idle task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xIdleTaskTCB; - static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Idle task's - state will be stored. */ - *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; - - /* Pass out the array that will be used as the Idle task's stack. */ - *ppxIdleTaskStackBuffer = uxIdleTaskStack; - - /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configMINIMAL_STACK_SIZE is specified in words, not bytes. */ - *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; -} - -/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the - * application must provide an implementation of vApplicationGetTimerTaskMemory() - * to provide the memory that is used by the Timer service task. */ -void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) -{ - /* If the buffers to be provided to the Timer task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xTimerTaskTCB; - static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Timer - task's state will be stored. */ - *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; - - /* Pass out the array that will be used as the Timer task's stack. */ - *ppxTimerTaskStackBuffer = uxTimerTaskStack; - - /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ - *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; -} - -#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X -#include "iodefine.h" -void vApplicationSetupTimerInterrupt(void) -{ - /* Enable CMT0 */ - unsigned short oldPRCR = SYSTEM.PRCR.WORD; - SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1); - MSTP(CMT0) = 0; - SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR; - - CMT0.CMCNT = 0; - CMT0.CMCOR = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128); - CMT0.CMCR.WORD = TU_BIT(6) | 2; - IR(CMT0, CMI0) = 0; - IPR(CMT0, CMI0) = configKERNEL_INTERRUPT_PRIORITY; - IEN(CMT0, CMI0) = 1; - CMT.CMSTR0.BIT.STR0 = 1; -} -#endif diff --git a/examples/device/audio_test_freertos/src/main.c b/examples/device/audio_test_freertos/src/main.c index 4e2264b5d..b1119c888 100644 --- a/examples/device/audio_test_freertos/src/main.c +++ b/examples/device/audio_test_freertos/src/main.c @@ -31,36 +31,37 @@ * $ python3 plot_audio_samples.py */ -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" - #include "freertos/semphr.h" #include "freertos/queue.h" + #include "freertos/semphr.h" #include "freertos/task.h" #include "freertos/timers.h" - #define USBD_STACK_SIZE 4096 + #define USBD_STACK_SIZE 4096 #else #include "FreeRTOS.h" - #include "semphr.h" #include "queue.h" + #include "semphr.h" #include "task.h" #include "timers.h" // Increase stack size when debug log is enabled - #define USBD_STACK_SIZE (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) + #define USBD_STACK_SIZE (4 * configMINIMAL_STACK_SIZE / 2) * (CFG_TUSB_DEBUG ? 2 : 1) #endif -#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE +#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE +#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES @@ -71,7 +72,7 @@ * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -82,34 +83,36 @@ enum { StackType_t blinky_stack[BLINKY_STACK_SIZE]; StaticTask_t blinky_taskdef; -StackType_t usb_device_stack[USBD_STACK_SIZE]; +StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; + +StackType_t audio_stack[AUDIO_STACK_SIZE]; +StaticTask_t audio_taskdef; #endif static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0 uint32_t sampFreq; uint8_t clkValid; // Range states -audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state -audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state +audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state +audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state // Audio test data -uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2]; +uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2]; uint16_t startVal = 0; -void led_blinking_task(void* param); -void usb_device_task(void* param); -void audio_task(void); +void led_blinking_task(void *param); +void usb_device_task(void *param); +void audio_isr_task(void *param); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // Init values @@ -126,40 +129,42 @@ int main(void) xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); // Create a task for tinyusb device stack - xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_device_stack, &usb_device_taskdef); + + // Audio receive (I2S) ISR simulation + // To simulate a ISR the priority is set to the highest + xTaskCreateStatic(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, audio_stack, &audio_taskdef); #else xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); #endif - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // only start scheduler for non-espressif mcu + #ifndef ESP_PLATFORM vTaskStartScheduler(); #endif return 0; } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) -void app_main(void) -{ +#ifdef ESP_PLATFORM +void app_main(void) { main(); } #endif // USB Device Driver task // This top level thread process all usb events and invoke callbacks -void usb_device_task(void* param) -{ +void usb_device_task(void *param) { (void) param; // init device stack on configured roothub port // This should be called after scheduler/kernel is started. // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -167,8 +172,7 @@ void usb_device_task(void* param) } // RTOS forever loop - while (1) - { + while (1) { // tinyusb device task tud_task(); } @@ -205,10 +209,18 @@ void tud_resume_cb(void) { // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ - // Yet to be filled - e.g. put meas data into TX FIFOs etc. - // asm("nop"); +// This task simulates an audio receive ISR, one frame is received every 1ms. +// We assume that the audio data is read from an I2S buffer. +// In a real application, this would be replaced with actual I2S receive callback. +void audio_isr_task(void *param) { + (void) param; + while (1) { + vTaskDelay(1); + for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) { + test_buffer_audio[cnt] = startVal++; + } + tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio)); + } } //--------------------------------------------------------------------+ @@ -216,8 +228,7 @@ void audio_task(void) //--------------------------------------------------------------------+ // Invoked when audio class specific set request received for an EP -bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -229,14 +240,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an interface -bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -248,14 +260,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; // Page 91 in UAC2 specification @@ -270,40 +283,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); // If request is for our feature unit - if ( entityID == 2 ) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Request uses format layout 1 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); - mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; + mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur; - TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); - return true; + TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); + return true; case AUDIO_FU_CTRL_VOLUME: // Request uses format layout 2 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); - volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur; - - TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); - return true; + volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur; + TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); + return true; // Unknown/Unsupported control default: TU_BREAKPOINT(); - return false; + return false; } } - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an EP -bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -311,14 +320,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an interface -bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -326,14 +336,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -343,12 +354,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * uint8_t entityID = TU_U16_HIGH(p_request->wIndex); // Input terminal (Microphone input) - if (entityID == 1) - { - switch ( ctrlSel ) - { - case AUDIO_TE_CTRL_CONNECTOR: - { + if (entityID == 1) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { // The terminal connector control only has a get request with only the CUR attribute. audio_desc_channel_cluster_t ret; @@ -357,11 +365,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * ret.bmChannelConfig = (audio_channel_config_t) 0; ret.iChannelNames = 0; - TU_LOG2(" Get terminal connector\r\n"); + TU_LOG1(" Get terminal connector\r\n"); - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); - } - break; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); + } break; // Unknown/Unsupported control selector default: @@ -371,43 +378,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Feature unit - if (entityID == 2) - { - switch ( ctrlSel ) - { + if (entityID == 2) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Audio control mute cur parameter block consists of only one byte - we thus can send it right away // There does not exist a range parameter block for mute - TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); + TU_LOG1(" Get Mute of channel: %u\r\n", channelNum); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1); case AUDIO_FU_CTRL_VOLUME: - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: - TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); + TU_LOG1(" Get Volume of channel: %u\r\n", channelNum); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); case AUDIO_CS_REQ_RANGE: - TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); + TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum); // Copy values - only for testing - better is version below audio_control_range_2_n_t(1) - ret; + ret; ret.wNumSubRanges = 1; - ret.subrange[0].bMin = -90; // -90 dB - ret.subrange[0].bMax = 90; // +90 dB - ret.subrange[0].bRes = 1; // 1 dB steps + ret.subrange[0].bMin = -90;// -90 dB + ret.subrange[0].bMax = 90; // +90 dB + ret.subrange[0].bRes = 1; // 1 dB steps - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; // Unknown/Unsupported control default: @@ -417,33 +421,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Clock Source unit - if ( entityID == 4 ) - { - switch ( ctrlSel ) - { + if (entityID == 4) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: // channelNum is always zero in this case - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: - TU_LOG2(" Get Sample Freq.\r\n"); - return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); + TU_LOG1(" Get Sample Freq.\r\n"); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); case AUDIO_CS_REQ_RANGE: - TU_LOG2(" Get Sample Freq. range\r\n"); - return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); + TU_LOG1(" Get Sample Freq. range\r\n"); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); - // Unknown/Unsupported control + // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; case AUDIO_CS_CTRL_CLK_VALID: // Only cur attribute exists for this request - TU_LOG2(" Get Sample Freq. valid\r\n"); - return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); + TU_LOG1(" Get Sample Freq. valid\r\n"); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); // Unknown/Unsupported control default: @@ -452,40 +453,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } } - TU_LOG2(" Unsupported entity: %d\r\n", entityID); - return false; // Yet not implemented -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2); - - return true; -} - -bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) n_bytes_copied; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++) - { - test_buffer_audio[cnt] = startVal++; - } - - return true; + TU_LOG1(" Unsupported entity: %d\r\n", entityID); + return false;// Yet not implemented } -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; (void) p_request; startVal = 0; @@ -496,7 +468,7 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void* param) { +void led_blinking_task(void *param) { (void) param; static uint32_t start_ms = 0; static bool led_state = false; @@ -507,6 +479,6 @@ void led_blinking_task(void* param) { start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state;// toggle } } diff --git a/examples/device/audio_test_freertos/src/plot_audio_samples.py b/examples/device/audio_test_freertos/src/plot_audio_samples.py index 46738eb3f..b6d8e824b 100755 --- a/examples/device/audio_test_freertos/src/plot_audio_samples.py +++ b/examples/device/audio_test_freertos/src/plot_audio_samples.py @@ -11,11 +11,11 @@ if __name__ == '__main__': # print(sd.query_devices()) fs = 48000 # Sample rate - duration = 1000e-3 # Duration of recording + duration = 3 # Duration of recording if platform.system() == 'Windows': # MME is needed since there are more than one MicNode device APIs (at least in Windows) - device = 'Microphone (MicNode) MME' + device = 'Microphone (MicNode), Windows WASAPI' elif platform.system() == 'Darwin': device = 'MicNode' else: diff --git a/examples/device/audio_test_freertos/src/tusb_config.h b/examples/device/audio_test_freertos/src/tusb_config.h index 8b376a4c3..b708e2541 100644 --- a/examples/device/audio_test_freertos/src/tusb_config.h +++ b/examples/device/audio_test_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ extern "C" { #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif @@ -123,7 +123,7 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor! #define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device #ifdef __cplusplus } diff --git a/examples/device/audio_test_multi_rate/CMakeLists.txt b/examples/device/audio_test_multi_rate/CMakeLists.txt index f61e1b640..6a7e68c3d 100644 --- a/examples/device/audio_test_multi_rate/CMakeLists.txt +++ b/examples/device/audio_test_multi_rate/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/audio_test_multi_rate/CMakePresets.json b/examples/device/audio_test_multi_rate/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/audio_test_multi_rate/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c index f9dcd1b8a..704a75b09 100644 --- a/examples/device/audio_test_multi_rate/src/main.c +++ b/examples/device/audio_test_multi_rate/src/main.c @@ -32,8 +32,8 @@ * $ python3 plot_audio_samples.py */ -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "bsp/board_api.h" @@ -49,7 +49,7 @@ * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum { +enum { BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, @@ -59,8 +59,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 +uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0 uint32_t sampFreq; uint8_t bytesPerSample; uint8_t clkValid; @@ -68,39 +68,36 @@ uint8_t clkValid; // Range states // List of supported sample rates static const uint32_t sampleRatesList[] = -{ - 32000, 48000, 96000 -}; + { + 32000, 48000, 96000}; -#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList) +#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList) // Bytes per format of every Alt settings static const uint8_t bytesPerSampleAltList[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = -{ - CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, - CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, + { + CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, + CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, }; -audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state +audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state // Audio test data -CFG_TUSB_MEM_ALIGN uint8_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX]; +CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[(TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX]; uint16_t startVal = 0; void led_blinking_task(void); void audio_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -111,9 +108,8 @@ int main(void) sampFreq = sampleRatesList[0]; clkValid = 1; - while (1) - { - tud_task(); // tinyusb device task + while (1) { + tud_task();// tinyusb device task led_blinking_task(); audio_task(); } @@ -127,29 +123,25 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } @@ -157,10 +149,29 @@ void tud_resume_cb(void) // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ - // Yet to be filled - e.g. put meas data into TX FIFOs etc. - // asm("nop"); +// This task simulates an audio receive callback, one frame is received every 1ms. +// We assume that the audio data is read from an I2S buffer. +// In a real application, this would be replaced with actual I2S receive callback. +void audio_task(void) { + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if (start_ms == curr_ms) return;// not enough time + start_ms = curr_ms; + // 16bit + if (bytesPerSample == 2) { + uint16_t *pData_16 = (uint16_t *) ((void *) test_buffer_audio); + for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) { + pData_16[cnt] = startVal++; + } + } + // 24bit in 32bit slot + else if (bytesPerSample == 4) { + uint32_t *pData_32 = (uint32_t *) ((void *) test_buffer_audio); + for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) { + pData_32[cnt] = (uint32_t) startVal++ << 16U; + } + } + tud_audio_write((uint8_t *) test_buffer_audio, (uint16_t) (sampFreq / 1000 * bytesPerSample)); } //--------------------------------------------------------------------+ @@ -168,23 +179,20 @@ void audio_task(void) //--------------------------------------------------------------------+ // Invoked when set interface is called, typically on start/stop streaming or format change -bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void)rhport; +bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + (void) rhport; //uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); // Clear buffer when streaming format is changed - if(alt != 0) - { - bytesPerSample = bytesPerSampleAltList[alt-1]; + if (alt != 0) { + bytesPerSample = bytesPerSampleAltList[alt - 1]; } return true; } // Invoked when audio class specific set request received for an EP -bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -196,14 +204,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an interface -bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; (void) pBuff; @@ -215,14 +224,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) -{ +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) { (void) rhport; // Page 91 in UAC2 specification @@ -237,49 +247,45 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); // If request is for our feature unit - if ( entityID == UAC2_ENTITY_FEATURE_UNIT ) - { - switch ( ctrlSel ) - { + if (entityID == UAC2_ENTITY_FEATURE_UNIT) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Request uses format layout 1 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); - mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; + mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur; TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); - return true; + return true; case AUDIO_FU_CTRL_VOLUME: // Request uses format layout 2 TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); - volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur; + volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur; TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); - return true; + return true; // Unknown/Unsupported control default: TU_BREAKPOINT(); - return false; + return false; } } // Clock Source unit - if ( entityID == UAC2_ENTITY_CLOCK ) - { - switch ( ctrlSel ) - { + if (entityID == UAC2_ENTITY_CLOCK) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t)); - sampFreq = (uint32_t)((audio_control_cur_4_t *)pBuff)->bCur; + sampFreq = (uint32_t) ((audio_control_cur_4_t *) pBuff)->bCur; TU_LOG2("Clock set current freq: %" PRIu32 "\r\n", sampFreq); return true; - break; + break; // Unknown/Unsupported control default: @@ -288,12 +294,11 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } } - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an EP -bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -301,16 +306,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t ep = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) ep; + (void) channelNum; + (void) ctrlSel; + (void) ep; // return tud_control_xfer(rhport, p_request, &tmp, 1); - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an interface -bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -318,14 +324,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); uint8_t itf = TU_U16_LOW(p_request->wIndex); - (void) channelNum; (void) ctrlSel; (void) itf; + (void) channelNum; + (void) ctrlSel; + (void) itf; - return false; // Yet not implemented + return false;// Yet not implemented } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; // Page 91 in UAC2 specification @@ -335,12 +342,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * uint8_t entityID = TU_U16_HIGH(p_request->wIndex); // Input terminal (Microphone input) - if (entityID == UAC2_ENTITY_INPUT_TERMINAL) - { - switch ( ctrlSel ) - { - case AUDIO_TE_CTRL_CONNECTOR: - { + if (entityID == UAC2_ENTITY_INPUT_TERMINAL) { + switch (ctrlSel) { + case AUDIO_TE_CTRL_CONNECTOR: { // The terminal connector control only has a get request with only the CUR attribute. audio_desc_channel_cluster_t ret; @@ -351,9 +355,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * TU_LOG2(" Get terminal connector\r\n"); - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); - } - break; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); + } break; // Unknown/Unsupported control selector default: @@ -363,43 +366,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Feature unit - if (entityID == UAC2_ENTITY_FEATURE_UNIT) - { - switch ( ctrlSel ) - { + if (entityID == UAC2_ENTITY_FEATURE_UNIT) { + switch (ctrlSel) { case AUDIO_FU_CTRL_MUTE: // Audio control mute cur parameter block consists of only one byte - we thus can send it right away // There does not exist a range parameter block for mute TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1); case AUDIO_FU_CTRL_VOLUME: - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); - return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); case AUDIO_CS_REQ_RANGE: TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); // Copy values - only for testing - better is version below audio_control_range_2_n_t(1) - ret; + ret; ret.wNumSubRanges = 1; - ret.subrange[0].bMin = -90; // -90 dB - ret.subrange[0].bMax = 30; // +30 dB - ret.subrange[0].bRes = 1; // 1 dB steps + ret.subrange[0].bMin = -90;// -90 dB + ret.subrange[0].bMax = 30; // +30 dB + ret.subrange[0].bRes = 1; // 1 dB steps - return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret)); // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; // Unknown/Unsupported control default: @@ -409,46 +409,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } // Clock Source unit - if ( entityID == UAC2_ENTITY_CLOCK ) - { - switch ( ctrlSel ) - { + if (entityID == UAC2_ENTITY_CLOCK) { + switch (ctrlSel) { case AUDIO_CS_CTRL_SAM_FREQ: // channelNum is always zero in this case - switch ( p_request->bRequest ) - { + switch (p_request->bRequest) { case AUDIO_CS_REQ_CUR: TU_LOG2(" Get Sample Freq.\r\n"); - return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); - case AUDIO_CS_REQ_RANGE: - { + case AUDIO_CS_REQ_RANGE: { TU_LOG2(" Get Sample Freq. range\r\n"); audio_control_range_4_n_t(N_sampleRates) rangef = - { - .wNumSubRanges = tu_htole16(N_sampleRates) - }; + { + .wNumSubRanges = tu_htole16(N_sampleRates)}; TU_LOG1("Clock get %d freq ranges\r\n", N_sampleRates); - for(uint8_t i = 0; i < N_sampleRates; i++) - { - rangef.subrange[i].bMin = (int32_t)sampleRatesList[i]; - rangef.subrange[i].bMax = (int32_t)sampleRatesList[i]; - rangef.subrange[i].bRes = 0; - TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); + for (uint8_t i = 0; i < N_sampleRates; i++) { + rangef.subrange[i].bMin = (int32_t) sampleRatesList[i]; + rangef.subrange[i].bMax = (int32_t) sampleRatesList[i]; + rangef.subrange[i].bRes = 0; + TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes); } return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef)); } - // Unknown/Unsupported control + // Unknown/Unsupported control default: TU_BREAKPOINT(); return false; } - break; + break; case AUDIO_CS_CTRL_CLK_VALID: // Only cur attribute exists for this request TU_LOG2(" Get Sample Freq. valid\r\n"); - return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); // Unknown/Unsupported control default: @@ -458,53 +452,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * } TU_LOG2(" Unsupported entity: %d\r\n", entityID); - return false; // Yet not implemented -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - tud_audio_write((uint8_t *)test_buffer_audio, (uint16_t)(sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000) * bytesPerSample)); - - return true; -} - -bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void) rhport; - (void) n_bytes_copied; - (void) itf; - (void) ep_in; - (void) cur_alt_setting; - - // 16bit - if(bytesPerSample == 2) - { - uint16_t* pData_16 = (uint16_t*)((void*)test_buffer_audio); - for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++) - { - pData_16[cnt] = startVal++; - } - } - // 24bit in 32bit slot - else if(bytesPerSample == 4) - { - uint32_t* pData_32 = (uint32_t*)((void*)test_buffer_audio); - for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++) - { - pData_32[cnt] = (uint32_t)startVal++ << 16U; - } - } - - return true; + return false;// Yet not implemented } -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; (void) p_request; startVal = 0; @@ -515,15 +466,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; // Blink every interval ms - if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + if (board_millis() - start_ms < blink_interval_ms) return;// not enough time start_ms += blink_interval_ms; board_led_write(led_state); - led_state = 1 - led_state; // toggle + led_state = 1 - led_state;// toggle } diff --git a/examples/device/audio_test_multi_rate/src/tusb_config.h b/examples/device/audio_test_multi_rate/src/tusb_config.h index 1c8288bce..d9b925dcf 100644 --- a/examples/device/audio_test_multi_rate/src/tusb_config.h +++ b/examples/device/audio_test_multi_rate/src/tusb_config.h @@ -133,7 +133,8 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example write FIFO every 1ms, so it should be 8 times larger for HS device + #ifdef __cplusplus } #endif diff --git a/examples/device/board_test/CMakeLists.txt b/examples/device/board_test/CMakeLists.txt index 012eff095..9a604a732 100644 --- a/examples/device/board_test/CMakeLists.txt +++ b/examples/device/board_test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -15,18 +15,23 @@ if(FAMILY STREQUAL "espressif") return() endif() -add_executable(${PROJECT}) +if (RTOS STREQUAL zephyr) + set(EXE_NAME app) +else() + set(EXE_NAME ${PROJECT}) + add_executable(${EXE_NAME}) +endif() # Example source -target_sources(${PROJECT} PUBLIC +target_sources(${EXE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ) # Example include -target_include_directories(${PROJECT} PUBLIC +target_include_directories(${EXE_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) # Configure compilation flags and libraries for the example without RTOS. # See the corresponding function in hw/bsp/FAMILY/family.cmake for details. -family_configure_device_example(${PROJECT} noos) +family_configure_device_example(${EXE_NAME} ${RTOS}) diff --git a/examples/device/board_test/CMakePresets.json b/examples/device/board_test/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/board_test/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/board_test/src/main.c b/examples/device/board_test/src/main.c index 91799eb89..d91a8760e 100644 --- a/examples/device/board_test/src/main.c +++ b/examples/device/board_test/src/main.c @@ -49,25 +49,34 @@ int main(void) { while (1) { uint32_t interval_ms = board_button_read() ? BLINK_PRESSED : BLINK_UNPRESSED; + int ch = board_getchar(); + if (ch > 0) { + board_putchar(ch); + #ifndef LOGGER_UART + board_uart_write(&ch, 1); + #endif + } + // Blink and print every interval ms if (!(board_millis() - start_ms < interval_ms)) { - board_uart_write(HELLO_STR, strlen(HELLO_STR)); - start_ms = board_millis(); + if (ch < 0) { + // skip if echoing + printf(HELLO_STR); + + #ifndef LOGGER_UART + board_uart_write(HELLO_STR, strlen(HELLO_STR)); + #endif + } + board_led_write(led_state); led_state = 1 - led_state; // toggle } - - // echo - uint8_t ch; - if (board_uart_read(&ch, 1) > 0) { - board_uart_write(&ch, 1); - } } } -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#ifdef ESP_PLATFORM void app_main(void) { main(); } diff --git a/examples/device/board_test/src/tusb_config.h b/examples/device/board_test/src/tusb_config.h index 89c27d1c0..81829d450 100644 --- a/examples/device/board_test/src/tusb_config.h +++ b/examples/device/board_test/src/tusb_config.h @@ -44,7 +44,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/cdc_dual_ports/CMakeLists.txt b/examples/device/cdc_dual_ports/CMakeLists.txt index f61e1b640..6a7e68c3d 100644 --- a/examples/device/cdc_dual_ports/CMakeLists.txt +++ b/examples/device/cdc_dual_ports/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/cdc_dual_ports/CMakePresets.json b/examples/device/cdc_dual_ports/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/cdc_dual_ports/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/cdc_dual_ports/src/main.c b/examples/device/cdc_dual_ports/src/main.c index 63ae8a8c9..5a3e18358 100644 --- a/examples/device/cdc_dual_ports/src/main.c +++ b/examples/device/cdc_dual_ports/src/main.c @@ -98,27 +98,33 @@ void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } - //--------------------------------------------------------------------+ // USB CDC //--------------------------------------------------------------------+ static void cdc_task(void) { - uint8_t itf; - - for (itf = 0; itf < CFG_TUD_CDC; itf++) { + for (uint8_t itf = 0; itf < CFG_TUD_CDC; itf++) { // connected() check for DTR bit // Most but not all terminal client set this when making connection // if ( tud_cdc_n_connected(itf) ) { if (tud_cdc_n_available(itf)) { uint8_t buf[64]; - uint32_t count = tud_cdc_n_read(itf, buf, sizeof(buf)); // echo back to both serial ports echo_serial_port(0, buf, count); echo_serial_port(1, buf, count); } + + // Press on-board button to send Uart status notification + static uint32_t btn_prev = 0; + static cdc_notify_uart_state_t uart_state = { .value = 0 }; + const uint32_t btn = board_button_read(); + if (!btn_prev && btn) { + uart_state.dsr ^= 1; + tud_cdc_notify_uart_state(&uart_state); + } + btn_prev = btn; } } } diff --git a/examples/device/cdc_dual_ports/src/tusb_config.h b/examples/device/cdc_dual_ports/src/tusb_config.h index 070f08ed1..7f7df3909 100644 --- a/examples/device/cdc_dual_ports/src/tusb_config.h +++ b/examples/device/cdc_dual_ports/src/tusb_config.h @@ -97,6 +97,8 @@ #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint + // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c index 7776eb958..bbcb479f5 100644 --- a/examples/device/cdc_dual_ports/src/usb_descriptors.c +++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c @@ -42,8 +42,7 @@ //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = -{ +tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = USB_BCD, @@ -68,16 +67,14 @@ tusb_desc_device_t const desc_device = // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor -uint8_t const * tud_descriptor_device_cb(void) -{ +uint8_t const *tud_descriptor_device_cb(void) { return (uint8_t const *) &desc_device; } //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ -enum -{ +enum { ITF_NUM_CDC_0 = 0, ITF_NUM_CDC_0_DATA, ITF_NUM_CDC_1, @@ -130,36 +127,32 @@ enum #define EPNUM_CDC_1_IN 0x84 #endif -uint8_t const desc_fs_configuration[] = -{ +uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 16, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 64), // 2nd CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 16, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 64), }; #if TUD_OPT_HIGH_SPEED // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration - -uint8_t const desc_hs_configuration[] = -{ +uint8_t const desc_hs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 8, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_0, 4, EPNUM_CDC_0_NOTIF, 16, EPNUM_CDC_0_OUT, EPNUM_CDC_0_IN, 512), // 2nd CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 8, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 512), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_1, 4, EPNUM_CDC_1_NOTIF, 16, EPNUM_CDC_1_OUT, EPNUM_CDC_1_IN, 512), }; // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed -tusb_desc_device_qualifier_t const desc_device_qualifier = -{ +tusb_desc_device_qualifier_t const desc_device_qualifier = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = USB_BCD, @@ -177,34 +170,31 @@ tusb_desc_device_qualifier_t const desc_device_qualifier = // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. // device_qualifier descriptor describes information about a high-speed capable device that would // change if the device were operating at the other speed. If not highspeed capable stall this request. -uint8_t const* tud_descriptor_device_qualifier_cb(void) -{ - return (uint8_t const*) &desc_device_qualifier; +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; } // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa -uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) -{ - (void) index; // for multiple configurations +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index;// for multiple configurations // if link speed is high return fullspeed config, and vice versa - return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; } -#endif // highspeed +#endif// highspeed // 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) -{ +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations #if TUD_OPT_HIGH_SPEED // Although we are highspeed, host may be fullspeed. - return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; #else return desc_fs_configuration; #endif @@ -223,8 +213,7 @@ enum { }; // array of pointer to string descriptors -char const *string_desc_arr[] = -{ +char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) "TinyUSB", // 1: Manufacturer "TinyUSB Device", // 2: Product @@ -254,14 +243,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // 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; + 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); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + if ( chr_count > max_count ) { chr_count = max_count; } // Convert ASCII string into UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { @@ -272,6 +261,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // first byte is length (including header), second byte is string type _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); - return _desc_str; } diff --git a/examples/device/cdc_msc/CMakeLists.txt b/examples/device/cdc_msc/CMakeLists.txt index 9415f8c68..b07f92342 100644 --- a/examples/device/cdc_msc/CMakeLists.txt +++ b/examples/device/cdc_msc/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.17) -set_property(GLOBAL PROPERTY USE_FOLDERS ON) +cmake_minimum_required(VERSION 3.20) +#set_property(GLOBAL PROPERTY USE_FOLDERS ON) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -16,20 +16,25 @@ if(FAMILY STREQUAL "espressif") return() endif() -add_executable(${PROJECT}) +if (RTOS STREQUAL zephyr) + set(EXE_NAME app) +else() + set(EXE_NAME ${PROJECT}) + add_executable(${EXE_NAME}) +endif() # Example source -target_sources(${PROJECT} PUBLIC +target_sources(${EXE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) # Example include -target_include_directories(${PROJECT} PUBLIC +target_include_directories(${EXE_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) -# Configure compilation flags and libraries for the example... see the corresponding function -# in hw/bsp/FAMILY/family.cmake for details. -family_configure_device_example(${PROJECT} noos) +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${EXE_NAME} ${RTOS}) diff --git a/examples/device/cdc_msc/CMakePresets.json b/examples/device/cdc_msc/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/cdc_msc/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/cdc_msc/prj.conf b/examples/device/cdc_msc/prj.conf new file mode 100644 index 000000000..2f5139d9d --- /dev/null +++ b/examples/device/cdc_msc/prj.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=y +CONFIG_FPU=y +CONFIG_NO_OPTIMIZATIONS=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_NRFX_POWER=y +CONFIG_NRFX_UARTE0=y diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c index f36c910d7..5cd93e7dd 100644 --- a/examples/device/cdc_msc/src/main.c +++ b/examples/device/cdc_msc/src/main.c @@ -119,6 +119,16 @@ void cdc_task(void) { tud_cdc_write(buf, count); tud_cdc_write_flush(); } + + // Press on-board button to send Uart status notification + static uint32_t btn_prev = 0; + static cdc_notify_uart_state_t uart_state = { .value = 0 }; + const uint32_t btn = board_button_read(); + if (!btn_prev && btn) { + uart_state.dsr ^= 1; + tud_cdc_notify_uart_state(&uart_state); + } + btn_prev = btn; } } diff --git a/examples/device/cdc_msc/src/msc_disk.c b/examples/device/cdc_msc/src/msc_disk.c index d2f8628f1..458681f58 100644 --- a/examples/device/cdc_msc/src/msc_disk.c +++ b/examples/device/cdc_msc/src/msc_disk.c @@ -40,17 +40,15 @@ static bool ejected = false; If you find any bugs or get any questions, feel free to file an\r\n\ issue at github.com/hathach/tinyusb" -enum -{ - DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount +enum { + DISK_BLOCK_NUM = 16,// 8KB is the smallest size that windows allow to mount DISK_BLOCK_SIZE = 512 }; #ifdef CFG_EXAMPLE_MSC_READONLY const #endif -uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = -{ +uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = { //------------- Block0: Boot Sector -------------// // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; // sector_per_cluster = 1; reserved_sectors = 1; @@ -59,60 +57,59 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = // drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29; // filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC"; // FAT magic code at offset 510-511 - { - 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, - 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' , - 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, +{ + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, + 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U', + 'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, - // Zero up to 2 last bytes of FAT magic code - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Zero up to 2 last bytes of FAT magic code + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA - }, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA}, //------------- Block1: FAT12 Table -------------// - { - 0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file +{ + 0xF8, 0xFF, 0xFF, 0xFF, 0x0F// // first 2 entries must be F8FF, third entry is cluster end of readme file }, //------------- Block2: Root Directory -------------// - { - // first entry is volume label - 'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // second entry is readme file - 'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D, - 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, - sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) +{ + // first entry is volume label + 'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // second entry is readme file + 'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D, + 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, + sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00// readme's files size (4 Bytes) }, //------------- Block3: Readme Content -------------// @@ -121,23 +118,21 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively -void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) -{ +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { (void) lun; const char vid[] = "TinyUSB"; const char pid[] = "Mass Storage"; const char rev[] = "1.0"; - memcpy(vendor_id , vid, strlen(vid)); - memcpy(product_id , pid, strlen(pid)); + memcpy(vendor_id, vid, strlen(vid)); + memcpy(product_id, pid, strlen(pid)); memcpy(product_rev, rev, strlen(rev)); } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted -bool tud_msc_test_unit_ready_cb(uint8_t lun) -{ +bool tud_msc_test_unit_ready_cb(uint8_t lun) { (void) lun; // RAM disk is ready until ejected @@ -152,29 +147,24 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size // Application update block count and block size -void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) -{ +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { (void) lun; *block_count = DISK_BLOCK_NUM; - *block_size = DISK_BLOCK_SIZE; + *block_size = DISK_BLOCK_SIZE; } // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage -bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) -{ +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { (void) lun; (void) power_condition; - if ( load_eject ) - { - if (start) - { + if (load_eject) { + if (start) { // load disk storage - }else - { + } else { // unload disk storage ejected = true; } @@ -185,45 +175,51 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. -int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) -{ +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + if (lba >= DISK_BLOCK_NUM) { + return -1; + } + + // Check for overflow of offset + bufsize + if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) { + return -1; + } - uint8_t const* addr = msc_disk[lba] + offset; + uint8_t const *addr = msc_disk[lba] + offset; memcpy(buffer, addr, bufsize); return (int32_t) bufsize; } -bool tud_msc_is_writable_cb (uint8_t lun) -{ +bool tud_msc_is_writable_cb(uint8_t lun) { (void) lun; -#ifdef CFG_EXAMPLE_MSC_READONLY + #ifdef CFG_EXAMPLE_MSC_READONLY return false; -#else + #else return true; -#endif + #endif } // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes -int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) -{ +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + if (lba >= DISK_BLOCK_NUM) return -1; -#ifndef CFG_EXAMPLE_MSC_READONLY - uint8_t* addr = msc_disk[lba] + offset; + #ifndef CFG_EXAMPLE_MSC_READONLY + uint8_t *addr = msc_disk[lba] + offset; memcpy(addr, buffer, bufsize); -#else - (void) lba; (void) offset; (void) buffer; -#endif + #else + (void) lba; + (void) offset; + (void) buffer; + #endif return (int32_t) bufsize; } @@ -231,42 +227,18 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks -int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) -{ - // read10 & write10 has their own callback and MUST not be handled here - - void const* response = NULL; - int32_t resplen = 0; +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + (void) buffer; + (void) bufsize; - // most scsi handled is input - bool in_xfer = true; - - switch (scsi_cmd[0]) - { + switch (scsi_cmd[0]) { default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // negative means error -> tinyusb could stall and/or response with failed status - resplen = -1; - break; - } - - // return resplen must not larger than bufsize - if ( resplen > bufsize ) resplen = bufsize; - - if ( response && (resplen > 0) ) - { - if(in_xfer) - { - memcpy(buffer, response, (size_t) resplen); - }else - { - // SCSI output - } + return -1; } - - return (int32_t) resplen; } #endif diff --git a/examples/device/cdc_msc/src/tusb_config.h b/examples/device/cdc_msc/src/tusb_config.h index 03e0e649c..811d464e9 100644 --- a/examples/device/cdc_msc/src/tusb_config.h +++ b/examples/device/cdc_msc/src/tusb_config.h @@ -87,7 +87,7 @@ //-------------------------------------------------------------------- #ifndef CFG_TUD_ENDPOINT0_SIZE -#define CFG_TUD_ENDPOINT0_SIZE 64 +#define CFG_TUD_ENDPOINT0_SIZE 64 #endif //------------- CLASS -------------// @@ -97,6 +97,8 @@ #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint + // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c index 4b6b88041..edcee0462 100644 --- a/examples/device/cdc_msc/src/usb_descriptors.c +++ b/examples/device/cdc_msc/src/usb_descriptors.c @@ -52,7 +52,6 @@ tusb_desc_device_t const desc_device = { .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = USB_VID, @@ -131,7 +130,7 @@ uint8_t const desc_fs_configuration[] = { TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), // Interface number, string index, EP Out & EP In address, EP size TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), @@ -146,7 +145,7 @@ uint8_t const desc_hs_configuration[] = { TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), // Interface number, string index, EP Out & EP In address, EP size TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), @@ -197,7 +196,6 @@ uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { #endif // highspeed - // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete @@ -256,14 +254,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // 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; + 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); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + if ( chr_count > max_count ) { chr_count = max_count; } // Convert ASCII string into UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { @@ -274,6 +272,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // first byte is length (including header), second byte is string type _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); - return _desc_str; } diff --git a/examples/device/cdc_msc_freertos/CMakeLists.txt b/examples/device/cdc_msc_freertos/CMakeLists.txt index 33c7a1ec0..f7636a07a 100644 --- a/examples/device/cdc_msc_freertos/CMakeLists.txt +++ b/examples/device/cdc_msc_freertos/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -19,7 +19,6 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c diff --git a/examples/device/cdc_msc_freertos/CMakePresets.json b/examples/device/cdc_msc_freertos/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/cdc_msc_freertos/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/cdc_msc_freertos/Makefile b/examples/device/cdc_msc_freertos/Makefile index fe3fb6482..10cff57a0 100644 --- a/examples/device/cdc_msc_freertos/Makefile +++ b/examples/device/cdc_msc_freertos/Makefile @@ -1,46 +1,16 @@ +RTOS = freertos include ../../build_system/make/make.mk -FREERTOS_SRC = lib/FreeRTOS-Kernel -FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC) - INC += \ src \ - src/FreeRTOSConfig \ $(TOP)/hw \ - $(TOP)/$(FREERTOS_SRC)/include \ - $(TOP)/$(FREERTOS_PORTABLE_SRC) \ # Example source EXAMPLE_SOURCE = \ - src/freertos_hook.c \ src/main.c \ src/msc_disk.c \ src/usb_descriptors.c SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) -# FreeRTOS source, all files in port folder -SRC_C += \ - $(FREERTOS_SRC)/list.c \ - $(FREERTOS_SRC)/queue.c \ - $(FREERTOS_SRC)/tasks.c \ - $(FREERTOS_SRC)/timers.c \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c)) - -SRC_S += \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s)) - -# include heap manage if configSUPPORT_DYNAMIC_ALLOCATION = 1 -# SRC_C += $(FREERTOS_SRC)/portable/MemMang/heap_1.c -# CFLAGS += -Wno-error=sign-compare - -# Suppress FreeRTOSConfig.h warnings -CFLAGS_GCC += -Wno-error=redundant-decls - -# Suppress FreeRTOS source warnings -CFLAGS_GCC += -Wno-error=cast-qual - -# FreeRTOS (lto + Os) linker issue -LDFLAGS_GCC += -Wl,--undefined=vTaskSwitchContext - include ../../build_system/make/rules.mk diff --git a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h deleted file mode 100644 index 869500ad2..000000000 --- a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FreeRTOS Kernel V10.0.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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. If you wish to use our Amazon - * FreeRTOS name, please do so in a fair use way that does not cause confusion. - * - * 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. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -// skip if included from IAR assembler -#ifndef __IASMARM__ - -// Include MCU header -#include "bsp/board_mcu.h" - -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 - #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" -#endif - -// TODO fix later -#if CFG_TUSB_MCU == OPT_MCU_MM32F327X - extern u32 SystemCoreClock; -#else - // FIXME cause redundant-decls warnings - extern uint32_t SystemCoreClock; -#endif - -#endif - -/* Cortex M23/M33 port configuration. */ -#define configENABLE_MPU 0 -#define configENABLE_FPU 1 -#define configENABLE_TRUSTZONE 0 -#define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) -#define configRUN_FREERTOS_SECURE_ONLY 1 - -#define configUSE_PREEMPTION 1 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configCPU_CLOCK_HZ SystemCoreClock -#define configTICK_RATE_HZ ( 1000 ) -#define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) -#define configMAX_TASK_NAME_LEN 16 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configQUEUE_REGISTRY_SIZE 4 -#define configUSE_QUEUE_SETS 0 -#define configUSE_TIME_SLICING 0 -#define configUSE_NEWLIB_REENTRANT 0 -#define configENABLE_BACKWARD_COMPATIBILITY 1 -#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 - -#define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 0 - -/* Hook function related definitions. */ -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning -#define configCHECK_FOR_STACK_OVERFLOW 2 -#define configCHECK_HANDLER_INSTALLATION 0 - -/* Run time and task stats gathering related definitions. */ -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 1 // legacy trace -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES 2 - -/* Software timer related definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) -#define configTIMER_QUEUE_LENGTH 32 -#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE - -/* Optional functions - most linkers will remove unused functions anyway. */ -#define INCLUDE_vTaskPrioritySet 0 -#define INCLUDE_uxTaskPriorityGet 0 -#define INCLUDE_vTaskDelete 0 -#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY -#define INCLUDE_xResumeFromISR 0 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 0 -#define INCLUDE_xTaskGetCurrentTaskHandle 0 -#define INCLUDE_uxTaskGetStackHighWaterMark 0 -#define INCLUDE_xTaskGetIdleTaskHandle 0 -#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 -#define INCLUDE_pcTaskGetTaskName 0 -#define INCLUDE_eTaskGetState 0 -#define INCLUDE_xEventGroupSetBitFromISR 0 -#define INCLUDE_xTimerPendFunctionCall 0 - -#ifdef __RX__ -/* Renesas RX series */ -#define vSoftwareInterruptISR INT_Excep_ICU_SWINT -#define vTickISR INT_Excep_CMT0_CMI0 -#define configPERIPHERAL_CLOCK_HZ (configCPU_CLOCK_HZ/2) -#define configKERNEL_INTERRUPT_PRIORITY 1 -#define configMAX_SYSCALL_INTERRUPT_PRIORITY 4 - -#else - -/* FreeRTOS hooks to NVIC vectors */ -#define xPortPendSVHandler PendSV_Handler -#define xPortSysTickHandler SysTick_Handler -#define vPortSVCHandler SVC_Handler - -//--------------------------------------------------------------------+ -// Interrupt nesting behavior configuration. -//--------------------------------------------------------------------+ -#if defined(__NVIC_PRIO_BITS) - // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h - #define configPRIO_BITS __NVIC_PRIO_BITS - -#elif defined(__ECLIC_INTCTLBITS) - // RISC-V Bumblebee core from nuclei - #define configPRIO_BITS __ECLIC_INTCTLBITS - -#elif defined(__IASMARM__) - // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS. - // Therefore we will hard coded it to minimum value of 2 to get pass ci build. - // IAR user must update this to correct value of the target MCU - #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU" - #define configPRIO_BITS 2 - -#else - #error "FreeRTOS configPRIO_BITS to be defined" -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<<configPRIO_BITS) - 1) - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -#endif - -#endif /* __FREERTOS_CONFIG__H */ diff --git a/examples/device/cdc_msc_freertos/src/freertos_hook.c b/examples/device/cdc_msc_freertos/src/freertos_hook.c deleted file mode 100644 index 4920e3fae..000000000 --- a/examples/device/cdc_msc_freertos/src/freertos_hook.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 -//--------------------------------------------------------------------+ -#include "FreeRTOS.h" -#include "task.h" -#include "common/tusb_common.h" - - -void vApplicationMallocFailedHook(void) -{ - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName) -{ - (void) pxTask; - (void) pcTaskName; - - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an - * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is - * used by the Idle task. */ -void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) -{ - /* If the buffers to be provided to the Idle task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xIdleTaskTCB; - static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Idle task's - state will be stored. */ - *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; - - /* Pass out the array that will be used as the Idle task's stack. */ - *ppxIdleTaskStackBuffer = uxIdleTaskStack; - - /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configMINIMAL_STACK_SIZE is specified in words, not bytes. */ - *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; -} - -/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the - * application must provide an implementation of vApplicationGetTimerTaskMemory() - * to provide the memory that is used by the Timer service task. */ -void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) -{ - /* If the buffers to be provided to the Timer task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xTimerTaskTCB; - static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Timer - task's state will be stored. */ - *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; - - /* Pass out the array that will be used as the Timer task's stack. */ - *ppxTimerTaskStackBuffer = uxTimerTaskStack; - - /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ - *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; -} - -#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X -#include "iodefine.h" -void vApplicationSetupTimerInterrupt(void) -{ - /* Enable CMT0 */ - unsigned short oldPRCR = SYSTEM.PRCR.WORD; - SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1); - MSTP(CMT0) = 0; - SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR; - - CMT0.CMCNT = 0; - CMT0.CMCOR = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128); - CMT0.CMCR.WORD = TU_BIT(6) | 2; - IR(CMT0, CMI0) = 0; - IPR(CMT0, CMI0) = configKERNEL_INTERRUPT_PRIORITY; - IEN(CMT0, CMI0) = 1; - CMT.CMSTR0.BIT.STR0 = 1; -} -#endif diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c index f70267e33..09ccc9bf3 100644 --- a/examples/device/cdc_msc_freertos/src/main.c +++ b/examples/device/cdc_msc_freertos/src/main.c @@ -30,24 +30,9 @@ #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF - // ESP-IDF need "freertos/" prefix in include path. - // CFG_TUSB_OS_INC_PATH should be defined accordingly. - #include "freertos/FreeRTOS.h" - #include "freertos/semphr.h" - #include "freertos/queue.h" - #include "freertos/task.h" - #include "freertos/timers.h" - +#ifdef ESP_PLATFORM #define USBD_STACK_SIZE 4096 #else - - #include "FreeRTOS.h" - #include "semphr.h" - #include "queue.h" - #include "task.h" - #include "timers.h" - // Increase stack size when debug log is enabled #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) #endif @@ -87,7 +72,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; static void usb_device_task(void *param); void led_blinking_task(void* param); void cdc_task(void *params); - +extern void msc_disk_init(void); //--------------------------------------------------------------------+ // Main //--------------------------------------------------------------------+ @@ -95,14 +80,10 @@ void cdc_task(void *params); int main(void) { board_init(); + // Create task for: tinyusb, blinky, cdc #if configSUPPORT_STATIC_ALLOCATION - // blinky task xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); - - // Create a task for tinyusb device stack xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); - - // Create CDC task xTaskCreateStatic(cdc_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, cdc_stack, &cdc_taskdef); #else xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); @@ -110,15 +91,15 @@ int main(void) { xTaskCreate(cdc_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL); #endif - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 -#if !TUP_MCU_ESPRESSIF +#ifndef ESP_PLATFORM + // only start scheduler for non-espressif mcu vTaskStartScheduler(); #endif return 0; } -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM void app_main(void) { main(); } @@ -142,6 +123,7 @@ static void usb_device_task(void *param) { board_init_after_tusb(); } + msc_disk_init(); // RTOS forever loop while (1) { // put this thread to waiting state until there is new events @@ -207,6 +189,16 @@ void cdc_task(void *params) { } tud_cdc_write_flush(); + + // Press on-board button to send Uart status notification + static uint32_t btn_prev = 0; + static cdc_notify_uart_state_t uart_state = { .value = 0 }; + const uint32_t btn = board_button_read(); + if (!btn_prev && btn) { + uart_state.dsr ^= 1; + tud_cdc_notify_uart_state(&uart_state); + } + btn_prev = btn; } // For ESP32-Sx this delay is essential to allow idle how to run and reset watchdog diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index d2f8628f1..d1ff2f71b 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,6 +28,37 @@ #if CFG_TUD_MSC +// Use async IO in example or not +#define CFG_EXAMPLE_MSC_ASYNC_IO 1 + +// Simulate read/write operation delay +#define CFG_EXAMPLE_MSC_IO_DELAY_MS 0 + +#if CFG_EXAMPLE_MSC_ASYNC_IO +#define IO_STACK_SIZE configMINIMAL_STACK_SIZE + +typedef struct { + uint8_t lun; + bool is_read; + uint32_t lba; + uint32_t offset; + void* buffer; + uint32_t bufsize; +} io_ops_t; + +QueueHandle_t io_queue; +#if configSUPPORT_STATIC_ALLOCATION +uint8_t io_queue_buf[sizeof(io_ops_t)]; +StaticQueue_t io_queue_static; +StackType_t io_stack[IO_STACK_SIZE]; +StaticTask_t io_taskdef; +#endif + +static void io_task(void *params); +#endif + +void msc_disk_init(void); + // whether host does safe-eject static bool ejected = false; @@ -40,8 +71,7 @@ static bool ejected = false; If you find any bugs or get any questions, feel free to file an\r\n\ issue at github.com/hathach/tinyusb" -enum -{ +enum { DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount DISK_BLOCK_SIZE = 512 }; @@ -119,16 +149,52 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = README_CONTENTS }; +#if CFG_EXAMPLE_MSC_ASYNC_IO +void msc_disk_init() { + +#if configSUPPORT_STATIC_ALLOCATION + io_queue = xQueueCreateStatic(1, sizeof(io_ops_t), io_queue_buf, &io_queue_static); + xTaskCreateStatic(io_task, "io", IO_STACK_SIZE, NULL, 2, io_stack, &io_taskdef); +#else + io_queue = xQueueCreate(1, sizeof(io_ops_t)); + xTaskCreate(io_task, "io", IO_STACK_SIZE, NULL, 2, NULL); +#endif +} + +static void io_task(void *params) { + (void) params; + io_ops_t io_ops; + while (1) { + if (xQueueReceive(io_queue, &io_ops, portMAX_DELAY)) { + const uint8_t* addr = msc_disk[io_ops.lba] + io_ops.offset; + int32_t nbytes = io_ops.bufsize; + if (io_ops.is_read) { + memcpy(io_ops.buffer, addr, io_ops.bufsize); + } else { +#ifndef CFG_EXAMPLE_MSC_READONLY + memcpy((uint8_t*) addr, io_ops.buffer, io_ops.bufsize); +#else + nbytes = -1; // failed to write +#endif + } + + tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); + tud_msc_async_io_done(nbytes, false); + } + } +} + +#else +void msc_disk_init() {} +#endif + // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively -void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) -{ +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { (void) lun; - const char vid[] = "TinyUSB"; const char pid[] = "Mass Storage"; const char rev[] = "1.0"; - memcpy(vendor_id , vid, strlen(vid)); memcpy(product_id , pid, strlen(pid)); memcpy(product_rev, rev, strlen(rev)); @@ -136,8 +202,7 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted -bool tud_msc_test_unit_ready_cb(uint8_t lun) -{ +bool tud_msc_test_unit_ready_cb(uint8_t lun) { (void) lun; // RAM disk is ready until ejected @@ -152,10 +217,8 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size // Application update block count and block size -void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) -{ +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { (void) lun; - *block_count = DISK_BLOCK_NUM; *block_size = DISK_BLOCK_SIZE; } @@ -163,18 +226,14 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage -bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) -{ +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { (void) lun; (void) power_condition; - if ( load_eject ) - { - if (start) - { + if (load_eject) { + if (start) { // load disk storage - }else - { + } else { // unload disk storage ejected = true; } @@ -185,83 +244,107 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes. -int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) -{ +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + if (lba >= DISK_BLOCK_NUM) { + return TUD_MSC_RET_ERROR; + } - uint8_t const* addr = msc_disk[lba] + offset; - memcpy(buffer, addr, bufsize); + // Check for overflow of offset + bufsize + if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) { + return TUD_MSC_RET_ERROR; + } - return (int32_t) bufsize; + #if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = {.is_read = true, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize}; + + // Send IO operation to IO task + TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); + + return TUD_MSC_RET_ASYNC; + #else + uint8_t const *addr = msc_disk[lba] + offset; + memcpy(buffer, addr, bufsize); + return bufsize; + #endif } -bool tud_msc_is_writable_cb (uint8_t lun) -{ +bool tud_msc_is_writable_cb (uint8_t lun) { (void) lun; -#ifdef CFG_EXAMPLE_MSC_READONLY + #ifdef CFG_EXAMPLE_MSC_READONLY return false; -#else + #else return true; -#endif + #endif } // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes -int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) -{ +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { + // out of ramdisk + if (lba >= DISK_BLOCK_NUM) { + return TUD_MSC_RET_ERROR; + } + + // Check for overflow of offset + bufsize + if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) { + return TUD_MSC_RET_ERROR; + } + + #ifdef CFG_EXAMPLE_MSC_READONLY (void) lun; + (void) buffer; + return bufsize; + #endif - // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + #if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = {.is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize}; -#ifndef CFG_EXAMPLE_MSC_READONLY - uint8_t* addr = msc_disk[lba] + offset; + // Send IO operation to IO task + TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); + + return TUD_MSC_RET_ASYNC; + #else + uint8_t *addr = msc_disk[lba] + offset; memcpy(addr, buffer, bufsize); -#else - (void) lba; (void) offset; (void) buffer; -#endif + tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); - return (int32_t) bufsize; + return bufsize; + #endif } // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks -int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) -{ +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { // read10 & write10 has their own callback and MUST not be handled here - void const* response = NULL; + void const *response = NULL; int32_t resplen = 0; // most scsi handled is input bool in_xfer = true; - switch (scsi_cmd[0]) - { + switch (scsi_cmd[0]) { default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // negative means error -> tinyusb could stall and/or response with failed status resplen = -1; - break; + break; } // return resplen must not larger than bufsize - if ( resplen > bufsize ) resplen = bufsize; + if (resplen > bufsize) { resplen = bufsize; } - if ( response && (resplen > 0) ) - { - if(in_xfer) - { + if (response && (resplen > 0)) { + if (in_xfer) { memcpy(buffer, response, (size_t) resplen); - }else - { + } else { // SCSI output } } diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index e743c9148..9cc3a18d1 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif @@ -104,6 +104,8 @@ #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 +#define CFG_TUD_CDC_NOTIFY 1 // Enable use of notification endpoint + // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c index 405a57fe4..a55fa3675 100644 --- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c +++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c @@ -52,7 +52,6 @@ tusb_desc_device_t const desc_device = { .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = USB_VID, @@ -131,7 +130,7 @@ uint8_t const desc_fs_configuration[] = TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), // Interface number, string index, EP Out & EP In address, EP size TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), @@ -147,7 +146,7 @@ uint8_t const desc_hs_configuration[] = TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512), // Interface number, string index, EP Out & EP In address, EP size TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), @@ -176,16 +175,14 @@ tusb_desc_device_qualifier_t const desc_device_qualifier = // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. // device_qualifier descriptor describes information about a high-speed capable device that would // change if the device were operating at the other speed. If not highspeed capable stall this request. -uint8_t const* tud_descriptor_device_qualifier_cb(void) -{ +uint8_t const* tud_descriptor_device_qualifier_cb(void) { return (uint8_t const*) &desc_device_qualifier; } // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa -uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) -{ +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { (void) index; // for multiple configurations // if link speed is high return fullspeed config, and vice versa @@ -204,13 +201,12 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) // 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) -{ +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations #if TUD_OPT_HIGH_SPEED // Although we are highspeed, host may be fullspeed. - return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; #else return desc_fs_configuration; #endif @@ -229,8 +225,7 @@ enum { }; // array of pointer to string descriptors -char const *string_desc_arr[] = -{ +char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) "TinyUSB", // 1: Manufacturer "TinyUSB Device", // 2: Product @@ -261,14 +256,14 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // 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; + 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); size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + if ( chr_count > max_count ) { chr_count = max_count; } // Convert ASCII string into UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { @@ -279,6 +274,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // first byte is length (including header), second byte is string type _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); - return _desc_str; } diff --git a/examples/device/cdc_uac2/CMakeLists.txt b/examples/device/cdc_uac2/CMakeLists.txt index 64e4374e9..c8c797637 100644 --- a/examples/device/cdc_uac2/CMakeLists.txt +++ b/examples/device/cdc_uac2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/cdc_uac2/CMakePresets.json b/examples/device/cdc_uac2/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/cdc_uac2/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/cdc_uac2/skip.txt b/examples/device/cdc_uac2/skip.txt new file mode 100644 index 000000000..a2a76af0e --- /dev/null +++ b/examples/device/cdc_uac2/skip.txt @@ -0,0 +1,8 @@ +mcu:LPC11UXX +mcu:LPC13XX +mcu:NUC121 +mcu:SAMD11 +mcu:SAME5X +mcu:SAMG +board:stm32l052dap52 +family:espressif diff --git a/examples/device/cdc_uac2/src/main.c b/examples/device/cdc_uac2/src/main.c index b148593da..bc87f6e3c 100644 --- a/examples/device/cdc_uac2/src/main.c +++ b/examples/device/cdc_uac2/src/main.c @@ -39,6 +39,7 @@ extern uint32_t blink_interval_ms; #endif void led_blinking_task(void); +void audio_task(void); /*------------- MAIN -------------*/ int main(void) @@ -62,7 +63,7 @@ int main(void) { tud_task(); // TinyUSB device task led_blinking_task(); - + audio_task(); #if (CFG_TUSB_MCU == OPT_MCU_RP2040) // printf("Hello, world!\r\n"); #endif diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h index 93489cf62..2318ccf73 100644 --- a/examples/device/cdc_uac2/src/tusb_config.h +++ b/examples/device/cdc_uac2/src/tusb_config.h @@ -145,8 +145,8 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN) #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device // EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 @@ -154,8 +154,8 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2 @@ -164,8 +164,11 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // CDC FIFO size of TX and RX -#define CFG_TUD_CDC_RX_BUFSIZE 64 -#define CFG_TUD_CDC_TX_BUFSIZE 64 +#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 70b0949a9..7d5da5ed0 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -49,17 +49,36 @@ uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 -// Buffer for microphone data -int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4]; // Buffer for speaker data int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4]; // Speaker data size received in the last frame -int spk_data_size; +uint16_t spk_data_size; // Resolution per format const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX}; // Current resolution, update on format change uint8_t current_resolution; +//--------------------------------------------------------------------+ +// AUDIO Task +//--------------------------------------------------------------------+ + +// This task simulates an audio transfer callback, one frame is sent/received every 1ms. +// In a real application, this would be replaced with actual I2S send/receive callback. +void audio_task(void) { + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if (start_ms == curr_ms) return;// not enough time + start_ms = curr_ms; + // When new data arrived, copy data from speaker buffer, to microphone buffer + // and send it over + // Only support speaker & headphone both have the same resolution + // If one is 16bit another is 24bit be care of LOUD noise ! + spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf)); + if (spk_data_size) { + tud_audio_write((uint8_t *) spk_buf, spk_data_size); + } +} + // Helper for clock get requests static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) { @@ -265,8 +284,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque blink_interval_ms = BLINK_STREAMING; } - // Clear buffer when streaming format is changed - spk_data_size = 0; if(alt != 0) { current_resolution = resolutions_per_format[alt-1]; @@ -275,30 +292,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque return true; } -bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)func_id; - (void)ep_out; - (void)cur_alt_setting; - - spk_data_size = tud_audio_read(spk_buf, n_bytes_received); - tud_audio_write(spk_buf, n_bytes_received); - - return true; -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)itf; - (void)ep_in; - (void)cur_alt_setting; - - // This callback could be used to fill microphone data separately - return true; -} - //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c index 9f7255d8a..22d3cf05a 100644 --- a/examples/device/cdc_uac2/src/usb_descriptors.c +++ b/examples/device/cdc_uac2/src/usb_descriptors.c @@ -116,7 +116,7 @@ uint8_t const * tud_descriptor_device_cb(void) #define EPNUM_CDC_IN 0x84 #endif -uint8_t const desc_configuration[] = +uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), @@ -128,13 +128,78 @@ uint8_t const desc_configuration[] = TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) }; +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80), + + // CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512) +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = 0x0100, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + + return desc_other_speed_config; +} + +#endif // highspeed + // 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; +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif } //--------------------------------------------------------------------+ diff --git a/examples/device/dfu/CMakeLists.txt b/examples/device/dfu/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/dfu/CMakeLists.txt +++ b/examples/device/dfu/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/dfu/CMakePresets.json b/examples/device/dfu/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/dfu/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/dfu_runtime/CMakeLists.txt b/examples/device/dfu_runtime/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/dfu_runtime/CMakeLists.txt +++ b/examples/device/dfu_runtime/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/dfu_runtime/CMakePresets.json b/examples/device/dfu_runtime/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/dfu_runtime/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/dynamic_configuration/CMakeLists.txt b/examples/device/dynamic_configuration/CMakeLists.txt index 2b20d2234..8a62d6ba2 100644 --- a/examples/device/dynamic_configuration/CMakeLists.txt +++ b/examples/device/dynamic_configuration/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/dynamic_configuration/CMakePresets.json b/examples/device/dynamic_configuration/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/dynamic_configuration/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/hid_boot_interface/CMakeLists.txt b/examples/device/hid_boot_interface/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/hid_boot_interface/CMakeLists.txt +++ b/examples/device/hid_boot_interface/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/hid_boot_interface/CMakePresets.json b/examples/device/hid_boot_interface/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/hid_boot_interface/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/hid_composite/CMakeLists.txt b/examples/device/hid_composite/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/hid_composite/CMakeLists.txt +++ b/examples/device/hid_composite/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/hid_composite/CMakePresets.json b/examples/device/hid_composite/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/hid_composite/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/hid_composite/src/main.c b/examples/device/hid_composite/src/main.c index 5302af3b8..a58107b6f 100644 --- a/examples/device/hid_composite/src/main.c +++ b/examples/device/hid_composite/src/main.c @@ -197,11 +197,43 @@ static void send_hid_report(uint8_t report_id, uint32_t btn) } } break; - default: break; } } +/* use this to send stylus touch signal through USB. */ +static void send_stylus_touch(uint16_t x, uint16_t y, bool state) +{ + // skip if hid is not ready yet + if ( !tud_hid_ready() ) return; + + static bool has_stylus_pen = false; + + hid_stylus_report_t report = + { + .attr = 0, + .x = 0, + .y = 0 + }; + + report.x = x; + report.y = y; + + if (state) + { + report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE; + tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); + + has_stylus_pen = true; + }else + { + report.attr = 0; + if (has_stylus_pen) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); + has_stylus_pen = false; + } + +} + // Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..) // tud_hid_report_complete_cb() is used to send the next report after previous one is complete void hid_task(void) @@ -209,6 +241,14 @@ void hid_task(void) // Poll every 10ms const uint32_t interval_ms = 10; static uint32_t start_ms = 0; + static uint32_t touch_ms = 0; + static bool touch_state = false; + + if (board_millis() - touch_ms < 100) { + touch_ms = board_millis(); + send_stylus_touch(0, 0, touch_state = !touch_state); + return; + } if ( board_millis() - start_ms < interval_ms) return; // not enough time start_ms += interval_ms; diff --git a/examples/device/hid_composite/src/usb_descriptors.c b/examples/device/hid_composite/src/usb_descriptors.c index e174db46d..15c6e1f73 100644 --- a/examples/device/hid_composite/src/usb_descriptors.c +++ b/examples/device/hid_composite/src/usb_descriptors.c @@ -79,6 +79,7 @@ uint8_t const desc_hid_report[] = { TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(REPORT_ID_KEYBOARD )), TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(REPORT_ID_MOUSE )), + TUD_HID_REPORT_DESC_STYLUS_PEN( HID_REPORT_ID(REPORT_ID_STYLUS_PEN )), TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(REPORT_ID_CONSUMER_CONTROL )), TUD_HID_REPORT_DESC_GAMEPAD ( HID_REPORT_ID(REPORT_ID_GAMEPAD )) }; diff --git a/examples/device/hid_composite/src/usb_descriptors.h b/examples/device/hid_composite/src/usb_descriptors.h index e733d31dd..18d31172b 100644 --- a/examples/device/hid_composite/src/usb_descriptors.h +++ b/examples/device/hid_composite/src/usb_descriptors.h @@ -29,6 +29,7 @@ enum { REPORT_ID_KEYBOARD = 1, REPORT_ID_MOUSE, + REPORT_ID_STYLUS_PEN, REPORT_ID_CONSUMER_CONTROL, REPORT_ID_GAMEPAD, REPORT_ID_COUNT diff --git a/examples/device/hid_composite_freertos/CMakeLists.txt b/examples/device/hid_composite_freertos/CMakeLists.txt index eb22014fb..6ce9e72fe 100644 --- a/examples/device/hid_composite_freertos/CMakeLists.txt +++ b/examples/device/hid_composite_freertos/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -19,7 +19,6 @@ add_executable(${PROJECT}) # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) diff --git a/examples/device/hid_composite_freertos/CMakePresets.json b/examples/device/hid_composite_freertos/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/hid_composite_freertos/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/hid_composite_freertos/Makefile b/examples/device/hid_composite_freertos/Makefile index 88d602393..bd625b345 100644 --- a/examples/device/hid_composite_freertos/Makefile +++ b/examples/device/hid_composite_freertos/Makefile @@ -1,47 +1,15 @@ -DEPS_SUBMODULES += lib/FreeRTOS-Kernel - +RTOS = freertos include ../../build_system/make/make.mk -FREERTOS_SRC = lib/FreeRTOS-Kernel -FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC) - INC += \ src \ - src/FreeRTOSConfig \ $(TOP)/hw \ - $(TOP)/$(FREERTOS_SRC)/include \ - $(TOP)/$(FREERTOS_PORTABLE_SRC) # Example source EXAMPLE_SOURCE = \ - src/freertos_hook.c \ src/main.c \ src/usb_descriptors.c SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) -# FreeRTOS source, all files in port folder -SRC_C += \ - $(FREERTOS_SRC)/list.c \ - $(FREERTOS_SRC)/queue.c \ - $(FREERTOS_SRC)/tasks.c \ - $(FREERTOS_SRC)/timers.c \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c)) - -SRC_S += \ - $(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s)) - -# include heap manage if configSUPPORT_DYNAMIC_ALLOCATION = 1 -# SRC_C += $(FREERTOS_SRC)/portable/MemMang/heap_1.c -# CFLAGS += -Wno-error=sign-compare - -# Suppress FreeRTOSConfig.h warnings -CFLAGS_GCC += -Wno-error=redundant-decls - -# Suppress FreeRTOS source warnings -CFLAGS_GCC += -Wno-error=cast-qual - -# FreeRTOS (lto + Os) linker issue -LDFLAGS_GCC += -Wl,--undefined=vTaskSwitchContext - include ../../build_system/make/rules.mk diff --git a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h deleted file mode 100644 index 869500ad2..000000000 --- a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * FreeRTOS Kernel V10.0.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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. If you wish to use our Amazon - * FreeRTOS name, please do so in a fair use way that does not cause confusion. - * - * 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. - * - * http://www.FreeRTOS.org - * http://aws.amazon.com/freertos - * - * 1 tab == 4 spaces! - */ - - -#ifndef FREERTOS_CONFIG_H -#define FREERTOS_CONFIG_H - -/*----------------------------------------------------------- - * Application specific definitions. - * - * These definitions should be adjusted for your particular hardware and - * application requirements. - * - * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE - * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. - * - * See http://www.freertos.org/a00110.html. - *----------------------------------------------------------*/ - -// skip if included from IAR assembler -#ifndef __IASMARM__ - -// Include MCU header -#include "bsp/board_mcu.h" - -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 - #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" -#endif - -// TODO fix later -#if CFG_TUSB_MCU == OPT_MCU_MM32F327X - extern u32 SystemCoreClock; -#else - // FIXME cause redundant-decls warnings - extern uint32_t SystemCoreClock; -#endif - -#endif - -/* Cortex M23/M33 port configuration. */ -#define configENABLE_MPU 0 -#define configENABLE_FPU 1 -#define configENABLE_TRUSTZONE 0 -#define configMINIMAL_SECURE_STACK_SIZE ( 1024 ) -#define configRUN_FREERTOS_SECURE_ONLY 1 - -#define configUSE_PREEMPTION 1 -#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 -#define configCPU_CLOCK_HZ SystemCoreClock -#define configTICK_RATE_HZ ( 1000 ) -#define configMAX_PRIORITIES ( 5 ) -#define configMINIMAL_STACK_SIZE ( 128 ) -#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) -#define configMAX_TASK_NAME_LEN 16 -#define configUSE_16_BIT_TICKS 0 -#define configIDLE_SHOULD_YIELD 1 -#define configUSE_MUTEXES 1 -#define configUSE_RECURSIVE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configQUEUE_REGISTRY_SIZE 4 -#define configUSE_QUEUE_SETS 0 -#define configUSE_TIME_SLICING 0 -#define configUSE_NEWLIB_REENTRANT 0 -#define configENABLE_BACKWARD_COMPATIBILITY 1 -#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 - -#define configSUPPORT_STATIC_ALLOCATION 1 -#define configSUPPORT_DYNAMIC_ALLOCATION 0 - -/* Hook function related definitions. */ -#define configUSE_IDLE_HOOK 0 -#define configUSE_TICK_HOOK 0 -#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning -#define configCHECK_FOR_STACK_OVERFLOW 2 -#define configCHECK_HANDLER_INSTALLATION 0 - -/* Run time and task stats gathering related definitions. */ -#define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 1 // legacy trace -#define configUSE_STATS_FORMATTING_FUNCTIONS 0 - -/* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES 2 - -/* Software timer related definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) -#define configTIMER_QUEUE_LENGTH 32 -#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE - -/* Optional functions - most linkers will remove unused functions anyway. */ -#define INCLUDE_vTaskPrioritySet 0 -#define INCLUDE_uxTaskPriorityGet 0 -#define INCLUDE_vTaskDelete 0 -#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY -#define INCLUDE_xResumeFromISR 0 -#define INCLUDE_vTaskDelayUntil 1 -#define INCLUDE_vTaskDelay 1 -#define INCLUDE_xTaskGetSchedulerState 0 -#define INCLUDE_xTaskGetCurrentTaskHandle 0 -#define INCLUDE_uxTaskGetStackHighWaterMark 0 -#define INCLUDE_xTaskGetIdleTaskHandle 0 -#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 -#define INCLUDE_pcTaskGetTaskName 0 -#define INCLUDE_eTaskGetState 0 -#define INCLUDE_xEventGroupSetBitFromISR 0 -#define INCLUDE_xTimerPendFunctionCall 0 - -#ifdef __RX__ -/* Renesas RX series */ -#define vSoftwareInterruptISR INT_Excep_ICU_SWINT -#define vTickISR INT_Excep_CMT0_CMI0 -#define configPERIPHERAL_CLOCK_HZ (configCPU_CLOCK_HZ/2) -#define configKERNEL_INTERRUPT_PRIORITY 1 -#define configMAX_SYSCALL_INTERRUPT_PRIORITY 4 - -#else - -/* FreeRTOS hooks to NVIC vectors */ -#define xPortPendSVHandler PendSV_Handler -#define xPortSysTickHandler SysTick_Handler -#define vPortSVCHandler SVC_Handler - -//--------------------------------------------------------------------+ -// Interrupt nesting behavior configuration. -//--------------------------------------------------------------------+ -#if defined(__NVIC_PRIO_BITS) - // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h - #define configPRIO_BITS __NVIC_PRIO_BITS - -#elif defined(__ECLIC_INTCTLBITS) - // RISC-V Bumblebee core from nuclei - #define configPRIO_BITS __ECLIC_INTCTLBITS - -#elif defined(__IASMARM__) - // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS. - // Therefore we will hard coded it to minimum value of 2 to get pass ci build. - // IAR user must update this to correct value of the target MCU - #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU" - #define configPRIO_BITS 2 - -#else - #error "FreeRTOS configPRIO_BITS to be defined" -#endif - -/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1<<configPRIO_BITS) - 1) - -/* The highest interrupt priority that can be used by any interrupt service -routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL -INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER -PRIORITY THAN THIS! (higher priorities are lower numeric values. */ -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2 - -/* Interrupt priorities used by the kernel port layer itself. These are generic -to all Cortex-M ports, and do not rely on any particular library functions. */ -#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! -See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ -#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) - -#endif - -#endif /* __FREERTOS_CONFIG__H */ diff --git a/examples/device/hid_composite_freertos/src/freertos_hook.c b/examples/device/hid_composite_freertos/src/freertos_hook.c deleted file mode 100644 index 4920e3fae..000000000 --- a/examples/device/hid_composite_freertos/src/freertos_hook.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 -//--------------------------------------------------------------------+ -#include "FreeRTOS.h" -#include "task.h" -#include "common/tusb_common.h" - - -void vApplicationMallocFailedHook(void) -{ - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName) -{ - (void) pxTask; - (void) pcTaskName; - - taskDISABLE_INTERRUPTS(); - TU_ASSERT(false, ); -} - -/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide an - * implementation of vApplicationGetIdleTaskMemory() to provide the memory that is - * used by the Idle task. */ -void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) -{ - /* If the buffers to be provided to the Idle task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xIdleTaskTCB; - static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Idle task's - state will be stored. */ - *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; - - /* Pass out the array that will be used as the Idle task's stack. */ - *ppxIdleTaskStackBuffer = uxIdleTaskStack; - - /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configMINIMAL_STACK_SIZE is specified in words, not bytes. */ - *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; -} - -/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the - * application must provide an implementation of vApplicationGetTimerTaskMemory() - * to provide the memory that is used by the Timer service task. */ -void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ) -{ - /* If the buffers to be provided to the Timer task are declared inside this - * function then they must be declared static - otherwise they will be allocated on - * the stack and so not exists after this function exits. */ - static StaticTask_t xTimerTaskTCB; - static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; - - /* Pass out a pointer to the StaticTask_t structure in which the Timer - task's state will be stored. */ - *ppxTimerTaskTCBBuffer = &xTimerTaskTCB; - - /* Pass out the array that will be used as the Timer task's stack. */ - *ppxTimerTaskStackBuffer = uxTimerTaskStack; - - /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer. - Note that, as the array is necessarily of type StackType_t, - configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */ - *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; -} - -#if CFG_TUSB_MCU == OPT_MCU_RX63X | CFG_TUSB_MCU == OPT_MCU_RX65X -#include "iodefine.h" -void vApplicationSetupTimerInterrupt(void) -{ - /* Enable CMT0 */ - unsigned short oldPRCR = SYSTEM.PRCR.WORD; - SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1); - MSTP(CMT0) = 0; - SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR; - - CMT0.CMCNT = 0; - CMT0.CMCOR = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128); - CMT0.CMCR.WORD = TU_BIT(6) | 2; - IR(CMT0, CMI0) = 0; - IPR(CMT0, CMI0) = configKERNEL_INTERRUPT_PRIORITY; - IEN(CMT0, CMI0) = 1; - CMT.CMSTR0.BIT.STR0 = 1; -} -#endif diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c index ae091571e..3f5e8a91c 100644 --- a/examples/device/hid_composite_freertos/src/main.c +++ b/examples/device/hid_composite_freertos/src/main.c @@ -31,7 +31,7 @@ #include "tusb.h" #include "usb_descriptors.h" -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -112,17 +112,16 @@ int main(void) xTimerStart(blinky_tm, 0); - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 -#if !TUP_MCU_ESPRESSIF + // only start scheduler for non-espressif mcu +#ifndef ESP_PLATFORM vTaskStartScheduler(); #endif return 0; } -#if TUP_MCU_ESPRESSIF -void app_main(void) -{ +#ifdef ESP_PLATFORM +void app_main(void) { main(); } #endif diff --git a/examples/device/hid_composite_freertos/src/tusb_config.h b/examples/device/hid_composite_freertos/src/tusb_config.h index 0689e0f23..b28033a0c 100644 --- a/examples/device/hid_composite_freertos/src/tusb_config.h +++ b/examples/device/hid_composite_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/hid_generic_inout/CMakeLists.txt b/examples/device/hid_generic_inout/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/hid_generic_inout/CMakeLists.txt +++ b/examples/device/hid_generic_inout/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/hid_generic_inout/CMakePresets.json b/examples/device/hid_generic_inout/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/hid_generic_inout/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/hid_multiple_interface/CMakeLists.txt b/examples/device/hid_multiple_interface/CMakeLists.txt index a01eb3456..3da8ee3df 100644 --- a/examples/device/hid_multiple_interface/CMakeLists.txt +++ b/examples/device/hid_multiple_interface/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/hid_multiple_interface/CMakePresets.json b/examples/device/hid_multiple_interface/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/hid_multiple_interface/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/midi_test/CMakeLists.txt b/examples/device/midi_test/CMakeLists.txt index e51f14c02..6a7e68c3d 100644 --- a/examples/device/midi_test/CMakeLists.txt +++ b/examples/device/midi_test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -9,22 +9,24 @@ project(${PROJECT} C CXX ASM) # Checks this example is valid for the family and initializes the project family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + # Espressif has its own cmake build system if(FAMILY STREQUAL "espressif") return() endif() add_executable(${PROJECT}) + # Example source target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) # Example include target_include_directories(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src - ) + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) # Configure compilation flags and libraries for the example without RTOS. # See the corresponding function in hw/bsp/FAMILY/family.cmake for details. diff --git a/examples/device/midi_test/CMakePresets.json b/examples/device/midi_test/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/midi_test/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/midi_test/src/main.c b/examples/device/midi_test/src/main.c index d7849a2c4..e5c47bdb2 100644 --- a/examples/device/midi_test/src/main.c +++ b/examples/device/midi_test/src/main.c @@ -58,8 +58,7 @@ void led_blinking_task(void); void midi_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port @@ -73,8 +72,7 @@ int main(void) board_init_after_tusb(); } - while (1) - { + while (1) { tud_task(); // tinyusb device task led_blinking_task(); midi_task(); @@ -86,29 +84,25 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ +void tud_suspend_cb(bool remote_wakeup_en) { (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } @@ -120,8 +114,7 @@ void tud_resume_cb(void) uint32_t note_pos = 0; // Store example melody as an array of note values -uint8_t note_sequence[] = -{ +const uint8_t note_sequence[] = { 74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78, 74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61, 56,61,64,68,74,78,81,86,90,93,98,102 @@ -137,11 +130,15 @@ void midi_task(void) // The MIDI interface always creates input and output port/jack descriptors // regardless of these being used or not. Therefore incoming traffic should be read // (possibly just discarded) to avoid the sender blocking in IO - uint8_t packet[4]; - while ( tud_midi_available() ) tud_midi_packet_read(packet); + while (tud_midi_available()) { + uint8_t packet[4]; + tud_midi_packet_read(packet); + } // send note periodically - if (board_millis() - start_ms < 286) return; // not enough time + if (board_millis() - start_ms < 286) { + return; // not enough time + } start_ms += 286; // Previous positions in the note sequence. @@ -149,7 +146,9 @@ void midi_task(void) // If we currently are at position 0, set the // previous position to the last note in the sequence. - if (previous < 0) previous = sizeof(note_sequence) - 1; + if (previous < 0) { + previous = sizeof(note_sequence) - 1; + } // Send Note On for current position at full velocity (127) on channel 1. uint8_t note_on[3] = { 0x90 | channel, note_sequence[note_pos], 127 }; @@ -163,7 +162,9 @@ void midi_task(void) note_pos++; // If we are at the end of the sequence, start over. - if (note_pos >= sizeof(note_sequence)) note_pos = 0; + if (note_pos >= sizeof(note_sequence)) { + note_pos = 0; + } } //--------------------------------------------------------------------+ diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c index 3870eaaf0..384742ae8 100644 --- a/examples/device/midi_test/src/usb_descriptors.c +++ b/examples/device/midi_test/src/usb_descriptors.c @@ -39,8 +39,7 @@ //--------------------------------------------------------------------+ // Device Descriptors //--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = -{ +tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, .bcdUSB = 0x0200, @@ -62,18 +61,13 @@ tusb_desc_device_t const desc_device = // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor -uint8_t const * tud_descriptor_device_cb(void) -{ +uint8_t const * tud_descriptor_device_cb(void) { return (uint8_t const *) &desc_device; } - - //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ - -enum -{ +enum { ITF_NUM_MIDI = 0, ITF_NUM_MIDI_STREAMING, ITF_NUM_TOTAL @@ -104,8 +98,7 @@ enum #define EPNUM_MIDI_IN 0x81 #endif -uint8_t const desc_fs_configuration[] = -{ +uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), @@ -114,8 +107,7 @@ uint8_t const desc_fs_configuration[] = }; #if TUD_OPT_HIGH_SPEED -uint8_t const desc_hs_configuration[] = -{ +uint8_t const desc_hs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), @@ -127,8 +119,7 @@ uint8_t const desc_hs_configuration[] = // 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) -{ +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations #if TUD_OPT_HIGH_SPEED @@ -152,8 +143,7 @@ enum { }; // array of pointer to string descriptors -char const *string_desc_arr[] = -{ +char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) "TinyUSB", // 1: Manufacturer "TinyUSB Device", // 2: Product @@ -182,14 +172,18 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // 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; + 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); - size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) { + chr_count = max_count; + } // Convert ASCII string into UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { diff --git a/examples/device/midi_test_freertos/CMakeLists.txt b/examples/device/midi_test_freertos/CMakeLists.txt new file mode 100644 index 000000000..6ce9e72fe --- /dev/null +++ b/examples/device/midi_test_freertos/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.20) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT} C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example with FreeRTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT} freertos) diff --git a/examples/device/midi_test_freertos/CMakePresets.json b/examples/device/midi_test_freertos/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/midi_test_freertos/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/midi_test_freertos/Makefile b/examples/device/midi_test_freertos/Makefile new file mode 100644 index 000000000..26cd83486 --- /dev/null +++ b/examples/device/midi_test_freertos/Makefile @@ -0,0 +1,15 @@ +RTOS = freertos +include ../../build_system/make/make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += \ + src/main.c \ + src/usb_descriptors.c \ + +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../build_system/make/rules.mk diff --git a/examples/device/midi_test_freertos/skip.txt b/examples/device/midi_test_freertos/skip.txt new file mode 100644 index 000000000..650bf355b --- /dev/null +++ b/examples/device/midi_test_freertos/skip.txt @@ -0,0 +1,16 @@ +mcu:CH32V103 +mcu:CH32V20X +mcu:CH32V307 +mcu:CXD56 +mcu:F1C100S +mcu:GD32VF103 +mcu:MCXA15 +mcu:MKL25ZXX +mcu:MSP430x5xx +mcu:RP2040 +mcu:SAMD11 +mcu:SAMX7X +mcu:VALENTYUSB_EPTRI +mcu:RAXXX +family:broadcom_32bit +family:broadcom_64bit diff --git a/examples/device/midi_test_freertos/src/CMakeLists.txt b/examples/device/midi_test_freertos/src/CMakeLists.txt new file mode 100644 index 000000000..9bd427c89 --- /dev/null +++ b/examples/device/midi_test_freertos/src/CMakeLists.txt @@ -0,0 +1,4 @@ +# This file is for ESP-IDF only +idf_component_register(SRCS main.c usb_descriptors.c + INCLUDE_DIRS "." + REQUIRES boards tinyusb_src) diff --git a/examples/device/midi_test_freertos/src/main.c b/examples/device/midi_test_freertos/src/main.c new file mode 100644 index 000000000..3e406d38d --- /dev/null +++ b/examples/device/midi_test_freertos/src/main.c @@ -0,0 +1,239 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "bsp/board_api.h" +#include "tusb.h" + +/* This MIDI example send sequence of note (on/off) repeatedly. To test on PC, you need to install + * synth software and midi connection management software. On + * - Linux (Ubuntu): install qsynth, qjackctl. Then connect TinyUSB output port to FLUID Synth input port + * - Windows: install MIDI-OX + * - MacOS: SimpleSynth + */ + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +#ifdef ESP_PLATFORM + #define USBD_STACK_SIZE 4096 +#else + // Increase stack size when debug log is enabled + #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) +#endif + +#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE +#define MIDI_STACK_SIZE configMINIMAL_STACK_SIZE + +// static task +#if configSUPPORT_STATIC_ALLOCATION +StackType_t blinky_stack[BLINKY_STACK_SIZE]; +StaticTask_t blinky_taskdef; + +StackType_t usb_device_stack[USBD_STACK_SIZE]; +StaticTask_t usb_device_taskdef; + +StackType_t midi_stack[MIDI_STACK_SIZE]; +StaticTask_t midi_taskdef; +#endif + +/* Blink pattern + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum { + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +void usb_device_task(void *param); +void led_blinking_task(void* param); +void midi_task(void* param); + +//--------------------------------------------------------------------+ +// Main +//--------------------------------------------------------------------+ +int main(void) { + board_init(); + +#if configSUPPORT_STATIC_ALLOCATION + xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(midi_task, "midi", MIDI_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, midi_stack, &midi_taskdef); +#else + xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); + xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(midi_task, "midi", MIDI_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL); +#endif + +#ifndef ESP_PLATFORM + // only start scheduler for non-espressif mcu + vTaskStartScheduler(); +#endif + + return 0; +} + +#ifdef ESP_PLATFORM +void app_main(void) { + main(); +} +#endif + +// USB Device Driver task +// This top level thread process all usb events and invoke callbacks +void usb_device_task(void *param) { + (void) param; + + // init device stack on configured roothub port + // This should be called after scheduler/kernel is started. + // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API. + tusb_rhport_init_t dev_init = { + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO + }; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + // RTOS forever loop + while (1) { + // put this thread to waiting state until there is new events + tud_task(); + } +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) { + blink_interval_ms = BLINK_NOT_MOUNTED; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) { + blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; +} + +//--------------------------------------------------------------------+ +// MIDI Task +//--------------------------------------------------------------------+ + +// Store example melody as an array of note values +const uint8_t note_sequence[] = { + 74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78, + 74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61, + 56,61,64,68,74,78,81,86,90,93,98,102 +}; + +void midi_task(void* param) { + (void) param; + + const uint8_t cable_num = 0; // MIDI jack associated with USB endpoint + const uint8_t channel = 0; // 0 for channel 1 + + // Variable that holds the current position in the sequence. + uint32_t note_pos = 0; + + while (1) { + // send note periodically + vTaskDelay(286 / portTICK_PERIOD_MS); + + // Previous positions in the note sequence. + int previous = (int) (note_pos - 1); + + // If we currently are at position 0, set the + // previous position to the last note in the sequence. + if (previous < 0) { + previous = sizeof(note_sequence) - 1; + } + + // Send Note On for current position at full velocity (127) on channel 1. + uint8_t note_on[3] = { 0x90 | channel, note_sequence[note_pos], 127 }; + tud_midi_stream_write(cable_num, note_on, 3); + + // Send Note Off for previous note. + uint8_t note_off[3] = { 0x80 | channel, note_sequence[previous], 0}; + tud_midi_stream_write(cable_num, note_off, 3); + + // Increment position + note_pos++; + + // If we are at the end of the sequence, start over. + if (note_pos >= sizeof(note_sequence)) { + note_pos = 0; + } + + // The MIDI interface always creates input and output port/jack descriptors + // regardless of these being used or not. Therefore incoming traffic should be read + // (possibly just discarded) to avoid the sender blocking in IO + while (tud_midi_available()) { + uint8_t packet[4]; + tud_midi_packet_read(packet); + } + } +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void* param) { + (void) param; + static uint32_t start_ms = 0; + static bool led_state = false; + + while (1) { + // Blink every interval ms + vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); + start_ms += blink_interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle + } +} diff --git a/examples/device/midi_test_freertos/src/tusb_config.h b/examples/device/midi_test_freertos/src/tusb_config.h new file mode 100644 index 000000000..0ffdc37fb --- /dev/null +++ b/examples/device/midi_test_freertos/src/tusb_config.h @@ -0,0 +1,108 @@ +/* + * 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 + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_FREERTOS +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* 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 0 +#define CFG_TUD_MIDI 1 +#define CFG_TUD_VENDOR 0 + +// MIDI FIFO size of TX and RX +#define CFG_TUD_MIDI_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_MIDI_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c new file mode 100644 index 000000000..384742ae8 --- /dev/null +++ b/examples/device/midi_test_freertos/src/usb_descriptors.c @@ -0,0 +1,199 @@ +/* + * 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 "bsp/board_api.h" +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +//--------------------------------------------------------------------+ +// 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 = 0xCafe, + .idProduct = USB_PID, + .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; +} +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +enum { + ITF_NUM_MIDI = 0, + ITF_NUM_MIDI_STREAMING, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_MIDI_OUT 0x02 + #define EPNUM_MIDI_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_CXD56 + // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number + // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN) + #define EPNUM_MIDI_OUT 0x02 + #define EPNUM_MIDI_IN 0x81 + +#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_MIDI_OUT 0x01 + #define EPNUM_MIDI_IN 0x82 + +#else + #define EPNUM_MIDI_OUT 0x01 + #define EPNUM_MIDI_IN 0x81 +#endif + +uint8_t const desc_fs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI_OUT, (0x80 | EPNUM_MIDI_IN), 64) +}; + +#if TUD_OPT_HIGH_SPEED +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI_OUT, (0x80 | EPNUM_MIDI_IN), 512) +}; +#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 + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, +}; + +// array of pointer to string descriptors +char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + NULL, // 3: Serials will use unique ID if possible +}; + +static uint16_t _desc_str[32 + 1]; + +// 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; + size_t chr_count; + + switch ( index ) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // 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); + const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) { + chr_count = max_count; + } + + // Convert ASCII string into UTF-16 + for ( size_t i = 0; i < chr_count; i++ ) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/examples/device/msc_dual_lun/CMakeLists.txt b/examples/device/msc_dual_lun/CMakeLists.txt index e69fead35..3955bfb49 100644 --- a/examples/device/msc_dual_lun/CMakeLists.txt +++ b/examples/device/msc_dual_lun/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) @@ -15,20 +15,25 @@ if(FAMILY STREQUAL "espressif") return() endif() -add_executable(${PROJECT}) +if (RTOS STREQUAL zephyr) + set(EXE_NAME app) +else() + set(EXE_NAME ${PROJECT}) + add_executable(${EXE_NAME}) +endif() # Example source -target_sources(${PROJECT} PUBLIC +target_sources(${EXE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_disk_dual.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) # Example include -target_include_directories(${PROJECT} PUBLIC +target_include_directories(${EXE_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) # Configure compilation flags and libraries for the example without RTOS. # See the corresponding function in hw/bsp/FAMILY/family.cmake for details. -family_configure_device_example(${PROJECT} noos) +family_configure_device_example(${EXE_NAME} ${RTOS}) diff --git a/examples/device/msc_dual_lun/CMakePresets.json b/examples/device/msc_dual_lun/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/msc_dual_lun/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/msc_dual_lun/prj.conf b/examples/device/msc_dual_lun/prj.conf new file mode 100644 index 000000000..2f5139d9d --- /dev/null +++ b/examples/device/msc_dual_lun/prj.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=y +CONFIG_FPU=y +CONFIG_NO_OPTIMIZATIONS=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_NRFX_POWER=y +CONFIG_NRFX_UARTE0=y diff --git a/examples/device/msc_dual_lun/src/msc_disk_dual.c b/examples/device/msc_dual_lun/src/msc_disk_dual.c index b44b77c6c..1f7fb98c7 100644 --- a/examples/device/msc_dual_lun/src/msc_disk_dual.c +++ b/examples/device/msc_dual_lun/src/msc_disk_dual.c @@ -55,8 +55,7 @@ If you find any bugs or get any questions, feel free to file an\r\n\ issue at github.com/hathach/tinyusb" -MSC_CONST uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = -{ +MSC_CONST uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = { //------------- Block0: Boot Sector -------------// // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; // sector_per_cluster = 1; reserved_sectors = 1; @@ -283,9 +282,11 @@ bool tud_msc_is_writable_cb(uint8_t lun) { // Process data in buffer to disk's storage and return number of written bytes int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { // out of ramdisk - if (lba >= DISK_BLOCK_NUM) return -1; + if (lba >= DISK_BLOCK_NUM) { + return -1; + } -#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY) + #if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY) (void) lun; (void) lba; (void) offset; @@ -302,11 +303,8 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 has their own callbacks (MUST not be handled here) int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { - void const* response = NULL; - int32_t resplen = 0; - - // most scsi handled is input - bool in_xfer = true; + (void) buffer; + (void) bufsize; switch (scsi_cmd[0]) { default: @@ -316,19 +314,6 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, u // negative means error -> tinyusb could stall and/or response with failed status return -1; } - - // return resplen must not larger than bufsize - if (resplen > bufsize) resplen = bufsize; - - if (response && (resplen > 0)) { - if (in_xfer) { - memcpy(buffer, response, (size_t) resplen); - } else { - // SCSI output - } - } - - return resplen; } #endif diff --git a/examples/device/net_lwip_webserver/CMakeLists.txt b/examples/device/net_lwip_webserver/CMakeLists.txt index 13923b583..87b92f4dc 100644 --- a/examples/device/net_lwip_webserver/CMakeLists.txt +++ b/examples/device/net_lwip_webserver/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_LIST_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/net_lwip_webserver/CMakePresets.json b/examples/device/net_lwip_webserver/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/net_lwip_webserver/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/net_lwip_webserver/Makefile b/examples/device/net_lwip_webserver/Makefile index 141532466..4ad110dec 100644 --- a/examples/device/net_lwip_webserver/Makefile +++ b/examples/device/net_lwip_webserver/Makefile @@ -1,5 +1,3 @@ -DEPS_SUBMODULES += lib/lwip - include ../../build_system/make/make.mk # suppress warning caused by lwip diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt index 5ebe71612..20a264387 100644 --- a/examples/device/net_lwip_webserver/skip.txt +++ b/examples/device/net_lwip_webserver/skip.txt @@ -10,6 +10,8 @@ mcu:SAMD11 mcu:STM32L0 mcu:STM32F0 mcu:KINETIS_KL +mcu:STM32H7RS +mcu:STM32N6 family:broadcom_64bit family:broadcom_32bit family:espressif diff --git a/examples/device/net_lwip_webserver/src/lwipopts.h b/examples/device/net_lwip_webserver/src/lwipopts.h index 41e8f0d67..04949cef9 100644 --- a/examples/device/net_lwip_webserver/src/lwipopts.h +++ b/examples/device/net_lwip_webserver/src/lwipopts.h @@ -58,6 +58,7 @@ #define LWIP_HTTPD_SSI_INCLUDE_TAG 0 #define LWIP_SINGLE_NETIF 1 +#define LWIP_NETIF_LINK_CALLBACK 1 #define PBUF_POOL_SIZE 4 diff --git a/examples/device/net_lwip_webserver/src/main.c b/examples/device/net_lwip_webserver/src/main.c index 8af7a9de6..4bdddf6c5 100644 --- a/examples/device/net_lwip_webserver/src/main.c +++ b/examples/device/net_lwip_webserver/src/main.c @@ -31,6 +31,12 @@ this appears as either a RNDIS or CDC-ECM USB virtual network adapter; the OS pi RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on Linux and macOS hosts The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server. + +Link State Control: +- Press the user button to toggle the network link state (UP/DOWN) +- This simulates "ethernet cable unplugged/plugged" events +- The host OS will see the network interface as disconnected/connected accordingly +- Use this to test network error handling and recovery in host applications */ /* Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers, @@ -63,9 +69,6 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00 /* lwip context */ static struct netif netif_data; -/* shared between tud_network_recv_cb() and service_traffic() */ -static struct pbuf *received_frame; - /* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ /* ideally speaking, this should be generated from the hardware's unique ID (if available) */ /* it is suggested that the first byte is 0x02 to indicate a link-local address */ @@ -137,6 +140,12 @@ static err_t netif_init_cb(struct netif *netif) { return ERR_OK; } +/* notifies the USB host about the link state change. */ +static void usbnet_netif_link_callback(struct netif *netif) { + bool link_up = netif_is_link_up(netif); + tud_network_link_state(BOARD_TUD_RHPORT, link_up); +} + static void init_lwip(void) { struct netif *netif = &netif_data; @@ -147,11 +156,19 @@ static void init_lwip(void) { memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address)); netif->hwaddr[5] ^= 0x01; - netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input); + netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ethernet_input); #if LWIP_IPV6 netif_create_ip6_linklocal_address(netif, 1); #endif netif_set_default(netif); + +#if LWIP_NETIF_LINK_CALLBACK + // Set the link callback to notify USB host about link state changes + netif_set_link_callback(netif, usbnet_netif_link_callback); + netif_set_link_up(netif); +#else + tud_network_link_state(BOARD_TUD_RHPORT, true); +#endif } /* handle any DNS requests from dns-server */ @@ -164,20 +181,29 @@ bool dns_query_proc(const char *name, ip4_addr_t *addr) { } bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { - /* this shouldn't happen, but if we get another packet before - parsing the previous, we must signal our inability to accept it */ - if (received_frame) return false; + struct netif *netif = &netif_data; if (size) { struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); - if (p) { - /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ - memcpy(p->payload, src, size); + if (p == NULL) { + printf("ERROR: Failed to allocate pbuf of size %d\n", size); + return false; + } + + /* Copy buf to pbuf */ + pbuf_take(p, src, size); - /* store away the pointer for service_traffic() to later handle */ - received_frame = p; + // Surrender ownership of our pbuf unless there was an error + // Only call pbuf_free if not Ok else it will panic with "pbuf_free: p->ref > 0" + // or steal it from whatever took ownership of it with undefined consequences. + // See: https://savannah.nongnu.org/patch/index.php?10121 + if (netif->input(p, netif) != ERR_OK) { + printf("ERROR: netif input failed\n"); + pbuf_free(p); } + // Signal tinyusb that the current frame has been processed. + tud_network_recv_renew(); } return true; @@ -191,24 +217,26 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { return pbuf_copy_partial(p, dst, p->tot_len, 0); } -static void service_traffic(void) { - /* handle any packet received by tud_network_recv_cb() */ - if (received_frame) { - ethernet_input(received_frame, &netif_data); - pbuf_free(received_frame); - received_frame = NULL; - tud_network_recv_renew(); - } - - sys_check_timeouts(); -} +static void handle_link_state_switch(void) { + /* Check for button press to toggle link state */ + static bool last_link_state = true; + static bool last_button_state = false; + bool current_button_state = board_button_read(); -void tud_network_init_cb(void) { - /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ - if (received_frame) { - pbuf_free(received_frame); - received_frame = NULL; + if (current_button_state && !last_button_state) { + /* Button pressed - toggle link state */ + last_link_state = !last_link_state; + if (last_link_state) { + printf("Link state: UP\n"); + netif_set_link_up(&netif_data); + } else { + printf("Link state: DOWN\n"); + netif_set_link_down(&netif_data); + } + /* LWIP callback will notify USB host about the change */ } + last_button_state = current_button_state; + } int main(void) { @@ -238,15 +266,23 @@ int main(void) { lwiperf_start_tcp_server_default(NULL, NULL); #endif +#if CFG_TUD_NCM + printf("USB NCM network interface initialized\n"); +#elif CFG_TUD_ECM_RNDIS + printf("USB RNDIS/ECM network interface initialized\n"); +#endif + while (1) { tud_task(); - service_traffic(); + sys_check_timeouts(); // service lwip + handle_link_state_switch(); } return 0; } /* lwip has provision for using a mutex, when applicable */ +/* This implementation is for single-threaded use only */ sys_prot_t sys_arch_protect(void) { return 0; } diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h index 22082fc81..c774f59ff 100644 --- a/examples/device/net_lwip_webserver/src/tusb_config.h +++ b/examples/device/net_lwip_webserver/src/tusb_config.h @@ -85,6 +85,7 @@ extern "C" { #endif // Use different configurations to test all net devices (also due to resource limitations) +#ifndef USE_ECM #if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54) #define USE_ECM 1 #elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML21, OPT_MCU_SAML22) @@ -97,6 +98,7 @@ extern "C" { #define USE_ECM 0 #define INCLUDE_IPERF #endif +#endif //-------------------------------------------------------------------- // NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index d061f5076..cd800f521 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -72,8 +72,11 @@ tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, +#if CFG_TUD_NCM + .bcdUSB = 0x0201, +#else .bcdUSB = 0x0200, - +#endif // Use Interface Association Descriptor (IAD) device class .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, @@ -188,6 +191,110 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; } +#if CFG_TUD_NCM +//--------------------------------------------------------------------+ +// BOS Descriptor +//--------------------------------------------------------------------+ + +/* Used to automatically load the NCM driver on Windows 10, otherwise manual driver install is needed. + Associate NCM interface with WINNCM driver. */ + +/* Microsoft OS 2.0 registry property descriptor +Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx +device should create DeviceInterfaceGUIDs. It can be done by driver and +in case of real PnP solution device should expose MS "Microsoft OS 2.0 +registry property descriptor". Such descriptor can insert any record +into Windows registry per device/configuration/interface. In our case it +will insert "DeviceInterfaceGUIDs" multistring property. + +GUID is freshly generated and should be OK to use. + +https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/ +(Section Microsoft OS compatibility descriptors) +*/ + +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) + +#define MS_OS_20_DESC_LEN 0xB2 + +// BOS Descriptor is required for webUSB +uint8_t const desc_bos[] = +{ + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 1), + + // Microsoft OS 2.0 descriptor + TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, 1) +}; + +uint8_t const * tud_descriptor_bos_cb(void) +{ + return desc_bos; +} + +uint8_t const desc_ms_os_20[] = +{ + // Set header: length, type, windows version, total length + U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), + + // Configuration subset header: length, type, configuration index, reserved, configuration total length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), + + // Function Subset header: length, type, first interface, reserved, subset length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_CDC, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), + + // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID + U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'N', 'C', 'M', 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible + + // MS OS 2.0 Registry property descriptor: length, type + U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), + U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, + U16_TO_U8S_LE(0x0050), // wPropertyDataLength + //bPropertyData: {12345678-0D08-43FD-8B3E-127CA8AFFF9D} + '{', 0x00, '1', 0x00, '2', 0x00, '3', 0x00, '4', 0x00, '5', 0x00, '6', 0x00, '7', 0x00, '8', 0x00, '-', 0x00, + '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, + '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, + '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { + // nothing to with DATA & ACK stage + if (stage != CONTROL_STAGE_SETUP) return true; + + switch (request->bmRequestType_bit.type) { + case TUSB_REQ_TYPE_VENDOR: + switch (request->bRequest) { + case 1: + if (request->wIndex == 7) { + // Get Microsoft OS 2.0 compatible descriptor + uint16_t total_len; + memcpy(&total_len, desc_ms_os_20 + 8, 2); + + return tud_control_xfer(rhport, request, (void*)(uintptr_t)desc_ms_os_20, total_len); + } else { + return false; + } + + default: break; + } + break; + + default: break; + } + + // stall unknown request + return false; +} + +#endif //--------------------------------------------------------------------+ // String Descriptors //--------------------------------------------------------------------+ diff --git a/examples/device/uac2_headset/CMakeLists.txt b/examples/device/uac2_headset/CMakeLists.txt index e92a57148..ced98a909 100644 --- a/examples/device/uac2_headset/CMakeLists.txt +++ b/examples/device/uac2_headset/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/uac2_headset/CMakePresets.json b/examples/device/uac2_headset/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/uac2_headset/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 6b2ab0973..33b8ea0ef 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -37,9 +37,9 @@ // List of supported sample rates const uint32_t sample_rates[] = {44100, 48000}; -uint32_t current_sample_rate = 44100; +uint32_t current_sample_rate = 44100; -#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) +#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) /* Blink pattern * - 25 ms : streaming data @@ -47,16 +47,14 @@ uint32_t current_sample_rate = 44100; * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum -{ +enum { BLINK_STREAMING = 25, BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, }; -enum -{ +enum { VOLUME_CTRL_0_DB = 0, VOLUME_CTRL_10_DB = 2560, VOLUME_CTRL_20_DB = 5120, @@ -75,8 +73,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 -int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 +int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 +int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0 // Buffer for microphone data int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4]; @@ -95,15 +93,13 @@ void audio_task(void); void audio_control_task(void); /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -112,9 +108,8 @@ int main(void) TU_LOG1("Headset running\r\n"); - while (1) - { - tud_task(); // TinyUSB device task + while (1) { + tud_task();// TinyUSB device task audio_task(); audio_control_task(); led_blinking_task(); @@ -126,70 +121,57 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ - (void)remote_wakeup_en; +void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } // Helper for clock get requests -static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) -{ +static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) { TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) - { - if (request->bRequest == AUDIO_CS_REQ_CUR) - { + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { + if (request->bRequest == AUDIO_CS_REQ_CUR) { TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate); - audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) }; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf)); - } - else if (request->bRequest == AUDIO_CS_REQ_RANGE) - { + audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf)); + } else if (request->bRequest == AUDIO_CS_REQ_RANGE) { audio_control_range_4_n_t(N_SAMPLE_RATES) rangef = - { - .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) - }; + { + .wNumSubRanges = tu_htole16(N_SAMPLE_RATES)}; TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); - for(uint8_t i = 0; i < N_SAMPLE_RATES; i++) - { + for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) { rangef.subrange[i].bMin = (int32_t) sample_rates[i]; rangef.subrange[i].bMax = (int32_t) sample_rates[i]; rangef.subrange[i].bRes = 0; - TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); + TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes); } - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef)); } - } - else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_1_t cur_valid = { .bCur = 1 }; + } else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && + request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t cur_valid = {.bCur = 1}; TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid)); } TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); @@ -197,25 +179,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t } // Helper for clock set requests -static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; +static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) { + (void) rhport; TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) - { + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); - current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; + current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur; TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate); return true; - } - else - { + } else { TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); return false; @@ -223,33 +201,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t } // Helper for feature unit get requests -static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) -{ +static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) { TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] }; + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]}; TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1)); - } - else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) - { - if (request->bRequest == AUDIO_CS_REQ_RANGE) - { + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1)); + } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { + if (request->bRequest == AUDIO_CS_REQ_RANGE) { audio_control_range_2_n_t(1) range_vol = { - .wNumSubRanges = tu_htole16(1), - .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } - }; + .wNumSubRanges = tu_htole16(1), + .subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}}; TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol)); - } - else if (request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) }; + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol)); + } else if (request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])}; TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol)); } } TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", @@ -259,35 +229,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req } // Helper for feature unit set requests -static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; +static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) { + (void) rhport; TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) - { + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); - mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur; + mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur; TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); return true; - } - else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) - { + } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); - volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; + volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur; TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); return true; - } - else - { + } else { TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); return false; @@ -299,16 +263,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req //--------------------------------------------------------------------+ // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + audio_control_request_t const *request = (audio_control_request_t const *) p_request; if (request->bEntityID == UAC2_ENTITY_CLOCK) return tud_audio_clock_get_request(rhport, request); if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) return tud_audio_feature_unit_get_request(rhport, request); - else - { + else { TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", request->bEntityID, request->bControlSelector, request->bRequest); } @@ -316,9 +278,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) { + audio_control_request_t const *request = (audio_control_request_t const *) p_request; if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) return tud_audio_feature_unit_set_request(rhport, request, buf); @@ -330,108 +291,82 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p return false; } -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void)rhport; +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + (void) rhport; uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) - blink_interval_ms = BLINK_MOUNTED; + blink_interval_ms = BLINK_MOUNTED; return true; } -bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void)rhport; +bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + (void) rhport; uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); TU_LOG2("Set interface %d alt %d\r\n", itf, alt); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) - blink_interval_ms = BLINK_STREAMING; + blink_interval_ms = BLINK_STREAMING; // Clear buffer when streaming format is changed spk_data_size = 0; - if(alt != 0) - { - current_resolution = resolutions_per_format[alt-1]; + if (alt != 0) { + current_resolution = resolutions_per_format[alt - 1]; } return true; } -bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)func_id; - (void)ep_out; - (void)cur_alt_setting; - - spk_data_size = tud_audio_read(spk_buf, n_bytes_received); - return true; -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)itf; - (void)ep_in; - (void)cur_alt_setting; - - // This callback could be used to fill microphone data separately - return true; -} - //--------------------------------------------------------------------+ // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ +// This task simulates an audio transfer callback, one frame is sent/received every 1ms. +// In a real application, this would be replaced with actual I2S send/receive callback. +void audio_task(void) { + static uint32_t start_ms = 0; + uint32_t curr_ms = board_millis(); + if (start_ms == curr_ms) return;// not enough time + start_ms = curr_ms; // When new data arrived, copy data from speaker buffer, to microphone buffer // and send it over // Only support speaker & headphone both have the same resolution // If one is 16bit another is 24bit be care of LOUD noise ! - if (spk_data_size) - { - if (current_resolution == 16) - { - int16_t *src = (int16_t*)spk_buf; - int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2; - int16_t *dst = (int16_t*)mic_buf; - while (src < limit) - { + spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf)); + if (spk_data_size) { + if (current_resolution == 16) { + int16_t *src = (int16_t *) spk_buf; + int16_t *limit = (int16_t *) spk_buf + spk_data_size / 2; + int16_t *dst = (int16_t *) mic_buf; + while (src < limit) { // Combine two channels into one int32_t left = *src++; int32_t right = *src++; *dst++ = (int16_t) ((left >> 1) + (right >> 1)); } - tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2)); + tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2)); spk_data_size = 0; - } - else if (current_resolution == 24) - { + } else if (current_resolution == 24) { int32_t *src = spk_buf; int32_t *limit = spk_buf + spk_data_size / 4; int32_t *dst = mic_buf; - while (src < limit) - { + while (src < limit) { // Combine two channels into one int32_t left = *src++; int32_t right = *src++; *dst++ = (int32_t) ((uint32_t) ((left >> 1) + (right >> 1)) & 0xffffff00ul); } - tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2)); + tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2)); spk_data_size = 0; } } } -void audio_control_task(void) -{ +void audio_control_task(void) { // Press on-board button to control volume // Open host volume control, volume should switch between 10% and 100% @@ -440,27 +375,25 @@ void audio_control_task(void) static uint32_t start_ms = 0; static uint32_t btn_prev = 0; - if ( board_millis() - start_ms < interval_ms) return; // not enough time + if (board_millis() - start_ms < interval_ms) return;// not enough time start_ms += interval_ms; uint32_t btn = board_button_read(); - if (!btn_prev && btn) - { + if (!btn_prev && btn) { // Adjust volume between 0dB (100%) and -30dB (10%) - for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) - { + for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) { volume[i] = volume[i] == 0 ? -VOLUME_CTRL_30_DB : 0; } // 6.1 Interrupt Data Message const audio_interrupt_data_t data = { - .bInfo = 0, // Class-specific interrupt, originated from an interface - .bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings - .wValue_cn_or_mcn = 0, // CH0: master volume - .wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change - .wIndex_ep_or_int = 0, // From the interface itself - .wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT, // From feature unit + .bInfo = 0, // Class-specific interrupt, originated from an interface + .bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings + .wValue_cn_or_mcn = 0, // CH0: master volume + .wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change + .wIndex_ep_or_int = 0, // From the interface itself + .wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit }; tud_audio_int_write(&data); @@ -472,8 +405,7 @@ void audio_control_task(void) //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h index 328e35f52..e738a09af 100644 --- a/examples/device/uac2_headset/src/tusb_config.h +++ b/examples/device/uac2_headset/src/tusb_config.h @@ -146,8 +146,8 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*2 #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device // EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 @@ -155,8 +155,8 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT)*2 #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2 diff --git a/examples/device/uac2_speaker_fb/CMakeLists.txt b/examples/device/uac2_speaker_fb/CMakeLists.txt index f40ca8716..0ed3db646 100644 --- a/examples/device/uac2_speaker_fb/CMakeLists.txt +++ b/examples/device/uac2_speaker_fb/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/uac2_speaker_fb/CMakePresets.json b/examples/device/uac2_speaker_fb/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/uac2_speaker_fb/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index ea5a2941d..12425b919 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -27,12 +27,12 @@ #include <string.h> #include "bsp/board_api.h" +#include "common_types.h" #include "tusb.h" #include "usb_descriptors.h" -#include "common_types.h" #ifdef CFG_QUIRK_OS_GUESSING -#include "quirk_os_guessing.h" + #include "quirk_os_guessing.h" #endif //--------------------------------------------------------------------+ @@ -41,14 +41,14 @@ // List of supported sample rates #if defined(__RX__) - const uint32_t sample_rates[] = {44100, 48000}; +const uint32_t sample_rates[] = {44100, 48000}; #else - const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; +const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; #endif -uint32_t current_sample_rate = 44100; +uint32_t current_sample_rate = 44100; -#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) +#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) /* Blink pattern * - 25 ms : streaming data @@ -56,16 +56,14 @@ uint32_t current_sample_rate = 44100; * - 1000 ms : device mounted * - 2500 ms : device is suspended */ -enum -{ +enum { BLINK_STREAMING = 25, BLINK_NOT_MOUNTED = 250, BLINK_MOUNTED = 1000, BLINK_SUSPENDED = 2500, }; -enum -{ +enum { VOLUME_CTRL_0_DB = 0, VOLUME_CTRL_10_DB = 2560, VOLUME_CTRL_20_DB = 5120, @@ -84,11 +82,11 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; // Audio controls // Current states -int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 -int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 +int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0 +int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0 // Buffer for speaker data -uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ/2]; +uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 2]; void led_blinking_task(void); void audio_task(void); @@ -96,20 +94,18 @@ void audio_task(void); #if CFG_AUDIO_DEBUG void audio_debug_task(void); uint8_t current_alt_settings; -uint16_t fifo_count; -uint32_t fifo_count_avg; +volatile uint16_t fifo_count; +volatile uint32_t fifo_count_avg; #endif /*------------- MAIN -------------*/ -int main(void) -{ +int main(void) { board_init(); // init device stack on configured roothub port tusb_rhport_init_t dev_init = { - .role = TUSB_ROLE_DEVICE, - .speed = TUSB_SPEED_AUTO - }; + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO}; tusb_init(BOARD_TUD_RHPORT, &dev_init); if (board_init_after_tusb) { @@ -118,9 +114,8 @@ int main(void) TU_LOG1("Speaker running\r\n"); - while (1) - { - tud_task(); // TinyUSB device task + while (1) { + tud_task();// TinyUSB device task led_blinking_task(); #if CFG_AUDIO_DEBUG audio_debug_task(); @@ -134,29 +129,25 @@ int main(void) //--------------------------------------------------------------------+ // Invoked when device is mounted -void tud_mount_cb(void) -{ +void tud_mount_cb(void) { blink_interval_ms = BLINK_MOUNTED; } // Invoked when device is unmounted -void tud_umount_cb(void) -{ +void tud_umount_cb(void) { blink_interval_ms = BLINK_NOT_MOUNTED; } // Invoked when usb bus is suspended // remote_wakeup_en : if host allow us to perform remote wakeup // Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ - (void)remote_wakeup_en; +void tud_suspend_cb(bool remote_wakeup_en) { + (void) remote_wakeup_en; blink_interval_ms = BLINK_SUSPENDED; } // Invoked when usb bus is resumed -void tud_resume_cb(void) -{ +void tud_resume_cb(void) { blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; } @@ -165,43 +156,34 @@ void tud_resume_cb(void) //--------------------------------------------------------------------+ // Helper for clock get requests -static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) -{ +static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) { TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) - { - if (request->bRequest == AUDIO_CS_REQ_CUR) - { + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { + if (request->bRequest == AUDIO_CS_REQ_CUR) { TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate); - audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) }; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf)); - } - else if (request->bRequest == AUDIO_CS_REQ_RANGE) - { + audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf)); + } else if (request->bRequest == AUDIO_CS_REQ_RANGE) { audio_control_range_4_n_t(N_SAMPLE_RATES) rangef = - { - .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) - }; + { + .wNumSubRanges = tu_htole16(N_SAMPLE_RATES)}; TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); - for(uint8_t i = 0; i < N_SAMPLE_RATES; i++) - { + for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) { rangef.subrange[i].bMin = (int32_t) sample_rates[i]; rangef.subrange[i].bMax = (int32_t) sample_rates[i]; rangef.subrange[i].bRes = 0; - TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); + TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes); } - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef)); } - } - else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_1_t cur_valid = { .bCur = 1 }; + } else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && + request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t cur_valid = {.bCur = 1}; TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid)); } TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); @@ -209,25 +191,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t } // Helper for clock set requests -static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; +static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) { + (void) rhport; TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) - { + if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); - current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; + current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur; TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate); return true; - } - else - { + } else { TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); return false; @@ -235,33 +213,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t } // Helper for feature unit get requests -static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) -{ +static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) { TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] }; + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]}; TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1)); - } - else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) - { - if (request->bRequest == AUDIO_CS_REQ_RANGE) - { + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1)); + } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { + if (request->bRequest == AUDIO_CS_REQ_RANGE) { audio_control_range_2_n_t(1) range_vol = { - .wNumSubRanges = tu_htole16(1), - .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } - }; + .wNumSubRanges = tu_htole16(1), + .subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}}; TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol)); - } - else if (request->bRequest == AUDIO_CS_REQ_CUR) - { - audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) }; + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol)); + } else if (request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])}; TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol)); } } TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", @@ -271,35 +241,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req } // Helper for feature unit set requests -static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; +static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) { + (void) rhport; TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) - { + if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); - mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur; + mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur; TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); return true; - } - else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) - { + } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); - volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; + volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur; TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); return true; - } - else - { + } else { TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", request->bEntityID, request->bControlSelector, request->bRequest); return false; @@ -307,16 +271,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req } // Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; +bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + audio_control_request_t const *request = (audio_control_request_t const *) p_request; if (request->bEntityID == UAC2_ENTITY_CLOCK) return tud_audio_clock_get_request(rhport, request); if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) return tud_audio_feature_unit_get_request(rhport, request); - else - { + else { TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", request->bEntityID, request->bControlSelector, request->bRequest); } @@ -324,9 +286,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p } // Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; +bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) { + audio_control_request_t const *request = (audio_control_request_t const *) p_request; if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) return tud_audio_feature_unit_set_request(rhport, request, buf); @@ -338,28 +299,26 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p return false; } -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void)rhport; +bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + (void) rhport; uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) - blink_interval_ms = BLINK_MOUNTED; + blink_interval_ms = BLINK_MOUNTED; return true; } -bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) -{ - (void)rhport; +bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { + (void) rhport; uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); TU_LOG2("Set interface %d alt %d\r\n", itf, alt); if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0) - blink_interval_ms = BLINK_STREAMING; + blink_interval_ms = BLINK_STREAMING; #if CFG_AUDIO_DEBUG current_alt_settings = alt; @@ -368,37 +327,34 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque return true; } -void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param) -{ - (void)func_id; - (void)alt_itf; +void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) { + (void) func_id; + (void) alt_itf; // Set feedback method to fifo counting feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT; feedback_param->sample_freq = current_sample_rate; } #if CFG_AUDIO_DEBUG -bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)n_bytes_received; - (void)func_id; - (void)ep_out; - (void)cur_alt_setting; +bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) { + (void) rhport; + (void) n_bytes_received; + (void) func_id; + (void) ep_out; + (void) cur_alt_setting; fifo_count = tud_audio_available(); // Same averaging method used in UAC2 class - fifo_count_avg = (uint32_t)(((uint64_t)fifo_count_avg * 63 + ((uint32_t)fifo_count << 16)) >> 6); + fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ((uint32_t) fifo_count << 16)) >> 6); return true; } #endif #if CFG_QUIRK_OS_GUESSING -bool tud_audio_feedback_format_correction_cb(uint8_t func_id) -{ - (void)func_id; - if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) { +bool tud_audio_feedback_format_correction_cb(uint8_t func_id) { + (void) func_id; + if (tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) { return true; } else { return false; @@ -409,25 +365,21 @@ bool tud_audio_feedback_format_correction_cb(uint8_t func_id) // AUDIO Task //--------------------------------------------------------------------+ -void audio_task(void) -{ - // Replace audio_task() with your I2S transmit callback. - // Here we simulate a callback called every 1ms. +// This task simulates an audio transmit callback, one frame is sent every 1ms. +// In a real application, this would be replaced with actual I2S transmit callback. +void audio_task(void) { static uint32_t start_ms = 0; uint32_t curr_ms = board_millis(); - if ( start_ms == curr_ms ) return; // not enough time + if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; - uint16_t length = (uint16_t) (current_sample_rate/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX); + uint16_t length = (uint16_t) (current_sample_rate / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX); - if (current_sample_rate == 44100 && (curr_ms % 10 == 0)) - { + if (current_sample_rate == 44100 && (curr_ms % 10 == 0)) { // Take one more sample every 10 cycles, to have a average reading speed of 44.1 // This correction is not needed in real world cases length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX; - } else - if (current_sample_rate == 88200 && (curr_ms % 5 == 0)) - { + } else if (current_sample_rate == 88200 && (curr_ms % 5 == 0)) { // Take one more sample every 5 cycles, to have a average reading speed of 88.2 // This correction is not needed in real world cases length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX; @@ -439,8 +391,7 @@ void audio_task(void) //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ -void led_blinking_task(void) -{ +void led_blinking_task(void) { static uint32_t start_ms = 0; static bool led_state = false; @@ -457,33 +408,30 @@ void led_blinking_task(void) // HID interface for audio debug //--------------------------------------------------------------------+ // Every 1ms, we will sent 1 debug information report -void audio_debug_task(void) -{ +void audio_debug_task(void) { static uint32_t start_ms = 0; uint32_t curr_ms = board_millis(); - if ( start_ms == curr_ms ) return; // not enough time + if (start_ms == curr_ms) return;// not enough time start_ms = curr_ms; audio_debug_info_t debug_info; - debug_info.sample_rate = current_sample_rate; - debug_info.alt_settings = current_alt_settings; - debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ; - debug_info.fifo_count = fifo_count; + debug_info.sample_rate = current_sample_rate; + debug_info.alt_settings = current_alt_settings; + debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ; + debug_info.fifo_count = fifo_count; debug_info.fifo_count_avg = (uint16_t) (fifo_count_avg >> 16); - for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) - { + for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) { debug_info.mute[i] = mute[i]; debug_info.volume[i] = volume[i]; } - if(tud_hid_ready()) + if (tud_hid_ready()) tud_hid_report(0, &debug_info, sizeof(debug_info)); } // Invoked when received GET_REPORT control request // Unused here -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) -{ +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) { // TODO not Implemented (void) itf; (void) report_id; @@ -496,8 +444,7 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t // Invoked when received SET_REPORT control request or // Unused here -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 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) { // This example doesn't use multiple report and report ID (void) itf; (void) report_id; diff --git a/examples/device/usbtmc/CMakeLists.txt b/examples/device/usbtmc/CMakeLists.txt index a63ca2d81..d2deb72d5 100644 --- a/examples/device/usbtmc/CMakeLists.txt +++ b/examples/device/usbtmc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/usbtmc/CMakePresets.json b/examples/device/usbtmc/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/usbtmc/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/video_capture/CMakeLists.txt b/examples/device/video_capture/CMakeLists.txt index 80dc39ca5..90788fa60 100644 --- a/examples/device/video_capture/CMakeLists.txt +++ b/examples/device/video_capture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/video_capture/CMakePresets.json b/examples/device/video_capture/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/video_capture/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c index aeeeb8930..0406279fd 100644 --- a/examples/device/video_capture/src/main.c +++ b/examples/device/video_capture/src/main.c @@ -185,7 +185,7 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) { #endif -void video_send_frame(void) { +static void video_send_frame(void) { static unsigned start_ms = 0; static unsigned already_sent = 0; @@ -292,7 +292,7 @@ void led_blinking_task(void* param) { #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE #define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4) -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define USBD_STACK_SIZE 4096 int main(void); void app_main(void) { @@ -351,8 +351,8 @@ void freertos_init_task(void) { xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL); #endif - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TUP_MCU_ESPRESSIF + // only start scheduler for non-espressif mcu + #ifndef ESP_PLATFORM vTaskStartScheduler(); #endif } diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h index 3a6daa3d3..4ba86ca65 100644 --- a/examples/device/video_capture/src/tusb_config.h +++ b/examples/device/video_capture/src/tusb_config.h @@ -58,7 +58,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/video_capture_2ch/CMakeLists.txt b/examples/device/video_capture_2ch/CMakeLists.txt index 80dc39ca5..90788fa60 100644 --- a/examples/device/video_capture_2ch/CMakeLists.txt +++ b/examples/device/video_capture_2ch/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/video_capture_2ch/CMakePresets.json b/examples/device/video_capture_2ch/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/video_capture_2ch/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c index dd6983766..dc616e3fa 100644 --- a/examples/device/video_capture_2ch/src/main.c +++ b/examples/device/video_capture_2ch/src/main.c @@ -300,7 +300,7 @@ void led_blinking_task(void* param) { #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE #define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4) -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define USBD_STACK_SIZE 4096 int main(void); void app_main(void) { @@ -359,8 +359,8 @@ void freertos_init_task(void) { xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL); #endif - // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TUP_MCU_ESPRESSIF + // only start scheduler for non-espressif mcu + #ifndef ESP_PLATFORM vTaskStartScheduler(); #endif } diff --git a/examples/device/video_capture_2ch/src/tusb_config.h b/examples/device/video_capture_2ch/src/tusb_config.h index 43c7dfc90..e84e49879 100644 --- a/examples/device/video_capture_2ch/src/tusb_config.h +++ b/examples/device/video_capture_2ch/src/tusb_config.h @@ -58,7 +58,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#ifdef ESP_PLATFORM #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/webusb_serial/CMakeLists.txt b/examples/device/webusb_serial/CMakeLists.txt index e92a57148..ced98a909 100644 --- a/examples/device/webusb_serial/CMakeLists.txt +++ b/examples/device/webusb_serial/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.20) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/examples/device/webusb_serial/CMakePresets.json b/examples/device/webusb_serial/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/webusb_serial/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} |
