Skip to content

Commit

Permalink
Merge branch 'skuep:master' into adc_dma
Browse files Browse the repository at this point in the history
  • Loading branch information
rhgndf authored Jan 9, 2025
2 parents 58454fc + e30e0bf commit ce4ed9d
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 102 deletions.
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ There is also a German 4-part article with instructions [here](https://dl-nordwe
## Compatibility
### Software
- [Direwolf](#notes-on-direwolf) as AX.25 modem/APRS en+decoder/...
- [AllStarLink](#notes-on-allstarlink-asl3) as ASL Node
- [APRSdroid](#notes-on-aprsdroid) as APRS en+decoder
- [CHIRP](#notes-on-chirp) for programming
- [VaraFM](#notes-on-varafm)
Expand All @@ -39,7 +40,8 @@ There is also a German 4-part article with instructions [here](https://dl-nordwe
### Tested Radios (so far)
- Wouxun UV-9D Mate (CHIRP + APRS)
- Baofeng UV-5R (CHIRP + APRS)
- BTECH 6X2 (CHIRP)
- BTECH 6X2 (CHIRP)
- Quansheng UV-K5 ([Quansheng Dock](https://github.com/nicsure/QuanshengDock), [Egzumer](https://github.com/egzumer/uv-k5-firmware-custom) or regular)

![Top side of PCB](doc/images/k1-aioc-photo.jpg?raw=true "Top side of PCB")

Expand Down Expand Up @@ -71,7 +73,7 @@ J2,D3,D4,R17 designators don't exist in the CPL file.

__Note__ for people doing their own PCB production: I suggest using the LCSC part numbers in the BOM file as a guide on what to buy (especially regarding the MCU).

__Note__ the current hardware version is **1.0**, but there is a pre-release **1.2** in the making [here](https://github.com/skuep/AIOC/pull/93) that features
__Note__ the current hardware version is **1.0**, but there is a version **1.2** currently in the making [here](https://github.com/skuep/AIOC/pull/93) that will feature
- support for an external hardware input (e.g. for COS)
- a split pad for the PTT/UART contact, so in case you would like to use the AIOC hardware for another purpose than a K1-style connector, you can separate these two signals that are otherwise connected to the same TRS contact.
- Adjustable input and output audio levels (line-level, mic-level)
Expand Down Expand Up @@ -146,7 +148,7 @@ Direwolf on Linux is confirmed working, please report any issues. Note that curr
While they are annoying, they are safe to ignore and require changes in the upstream direwolf sourcecode. See https://github.com/wb2osz/direwolf/issues/448 for more details.

## Notes on Direwolf
- Follow the regular setup guide with direwolf to determine the correct audio device to use.
- Follow the regular setup guide with [Direwolf](https://github.com/wb2osz/direwolf) to determine the correct audio device to use.
For the serial and CM108 PTT interfaces on Linux, you need to set correct permissions on the ttyACM/hidraw devices. Consult Direwolf manual.
- Configure the device as follows
````
Expand All @@ -160,8 +162,25 @@ While they are annoying, they are safe to ignore and require changes in the upst
[...]
````

## Notes on AllStarLink (ASL3)
Once your cable is emulating a CM108, it becomes quite simple to plug into your HT and setup a simple simplex AllStarLink node that talks to your favorite repeater or node.

In `asl-menu`, set:
- Device type: `usbradio` (menu sections: 2 > A1 > N4 > I2)
- Duplex type: `1` (menu sections: 2 > A1 > N5)

Edit `usbradio.conf` (menu sections: 6 > H) (for a Puxing PX-888 cheap Chineese HT)
- `rxboost = 0`
- `rxsqhyst = 500`
- `carrierfrom = usbinvert`
- `ctcssfrom = no`
- `rxdemod = speaker`
- `txprelim = no`
- `invertptt = no`


## Notes on APRSdroid
APRSdroid support has been added by AIOC by implementing support for the fixed 22050 Hz sample rate that APRSdroid requires.
[APRSdroid](https://aprsdroid.org/) support has been added by AIOC by implementing support for the fixed 22050 Hz sample rate that APRSdroid requires.
It is important to notice, that the exact sample rate can not be achieved by the hardware, due to the 8 MHz crystal.
The actual sample rate used is 22052 Hz (which represents around 90 ppm of error). From my testing this does not seem to be a problem for APRS at all.

Expand Down
2 changes: 1 addition & 1 deletion stm32/aioc-fw/Inc/stm32f3xx_hal_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
#define HAL_TSC_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
#define HAL_USART_MODULE_ENABLED
/* #define HAL_WWDG_MODULE_ENABLED */
#define HAL_WWDG_MODULE_ENABLED

/* ########################## HSE/HSI Values adaptation ##################### */
/**
Expand Down
2 changes: 1 addition & 1 deletion stm32/aioc-fw/Middlewares/Third-Party/tinyusb
Submodule tinyusb updated 996 files
15 changes: 14 additions & 1 deletion stm32/aioc-fw/Src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ static void SystemReset(void) {
#endif
}

if (resetFlags & RCC_CSR_IWDGRSTF) {
if (resetFlags & RCC_CSR_WWDGRSTF) {
#if defined(SYSTEM_MEMORY_BASE)
/* Reset cause was watchdog, which is used for rebooting into the bootloader.
Set stack pointer to *SYSTEM_MEMORY_BASE
Expand Down Expand Up @@ -189,6 +189,17 @@ int main(void)

USB_Init();

/* Enable indepedent watchdog to reset on lockup*/
IWDG_HandleTypeDef IWDGHandle = {
.Instance = IWDG,
.Init = {
.Prescaler = IWDG_PRESCALER_8,
.Reload = 0x0FFF,
.Window = 0x0FFF
}
};
HAL_IWDG_Init(&IWDGHandle);

uint32_t i = 0;
while (1) {
USB_Task();
Expand All @@ -206,6 +217,8 @@ int main(void)
fb.feedbackMin, fb.feedbackMax, fb.feedbackAvg);
#endif
}

HAL_IWDG_Refresh(&IWDGHandle);
}

return 0;
Expand Down
38 changes: 0 additions & 38 deletions stm32/aioc-fw/Src/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,6 @@
#include "usb_audio.h"
#include "usb_hid.h"

// We have ISOCHRONOUS endpoints defined that share the same endpoint number, but have opposing directions.
// However with STM32 hardware, ISOCHRONOUS endpoints use both RX and TX structures of the same endpoint register in hardware
// We circumvent a clash by defining our own custom endpoint map for the tiny usb stack.
// This callback is probably not needed with new versions of tinyusb
uint8_t tu_stm32_edpt_number_cb(uint8_t addr)
{
switch (addr) {
case 0x00:
case 0x80:
/* Endpoint Zero */
return 0x00;

case EPNUM_AUDIO_IN:
return 0x01;

case EPNUM_AUDIO_OUT:
return 0x02;

case EPNUM_AUDIO_FB:
return 0x03;

case EPNUM_HID_IN:
case EPNUM_HID_OUT:
return 0x04;

case EPNUM_CDC_0_OUT:
case EPNUM_CDC_0_IN:
return 0x05;

case EPNUM_CDC_0_NOTIF:
return 0x06;

default:
TU_BREAKPOINT();
return 0x00;
}
}

// FIXME: Do all three need to be handled, or just the LP one?
// USB high-priority interrupt (Channel 74): Triggered only by a correct
// transfer event for isochronous and double-buffer bulk transfer to reach
Expand Down
11 changes: 11 additions & 0 deletions stm32/aioc-fw/Src/usb_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,17 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba
feedback_param->method = AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED;
}

bool tud_audio_feedback_format_correction_cb(uint8_t func_id)
{
/* Use the quirk detection to detect whether we need format correction (10.14) according to the USB specification (MacOS)
* or whether we use no correction (16.16) as a quirk (Windows). Linux works either way. */
if (tud_speed_get() == TUSB_SPEED_FULL) {
return USB_DescUAC2Quirk() ? false : true;
} else {
return false;
}
}

TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift)
{
static uint32_t prev_cycles = 0;
Expand Down
146 changes: 97 additions & 49 deletions stm32/aioc-fw/Src/usb_descriptors.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@
#include "settings.h"
#include "stm32f3xx_hal.h"

/* For quirk detection, borrowed from tinyusb uac2_speaker_fb example */
static tusb_desc_type_t desc_req_buf[2];
static int desc_req_idx = 0;

void quirk_host_os_hint_desc_cb(tusb_desc_type_t desc) {
if (desc == TUSB_DESC_DEVICE) {
desc_req_idx = 0;
} else if (desc_req_idx < 2 && (desc == TUSB_DESC_CONFIGURATION || desc == TUSB_DESC_BOS || desc == TUSB_DESC_STRING)) {
// Skip redundant request
if (desc_req_idx == 0 || (desc_req_idx > 0 && desc_req_buf[desc_req_idx - 1] != desc)) {
desc_req_buf[desc_req_idx++] = desc;
}
}
}



//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -54,6 +71,8 @@ uint8_t const* tud_descriptor_device_cb(void) {
desc_device.idVendor = (settingsRegMap[SETTINGS_REG_USBID] & SETTINGS_REG_USBID_VID_MASK) >> SETTINGS_REG_USBID_VID_OFFS;
desc_device.idProduct = (settingsRegMap[SETTINGS_REG_USBID] & SETTINGS_REG_USBID_PID_MASK) >> SETTINGS_REG_USBID_PID_OFFS;

quirk_host_os_hint_desc_cb(TUSB_DESC_DEVICE);

return (uint8_t const*) &desc_device;
}

Expand Down Expand Up @@ -125,57 +144,65 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
AIOC_DFU_RT_DESC_LEN \
)

uint8_t const desc_fs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(
/* config_num */ 1,
/* _itfcount */ ITF_NUM_TOTAL,
/* _stridx */ 0x00,
/* _total_len */ CONFIG_TOTAL_LEN,
/* _attribute */ 0x00,
/* _power_ma */ 100
),

AIOC_AUDIO_DESCRIPTOR(
/* _itfnum */ ITF_NUM_AUDIO_CONTROL,
/* _stridx */ STR_IDX_AUDIOITF,
/* _nBytesPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE,
/* _nBitsUsedPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE*8,
/* _epin */ EPNUM_AUDIO_IN,
/* _epinsize */ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX,
/* _epout */ EPNUM_AUDIO_OUT,
/* _epoutsize */ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX,
/* _epfb */ EPNUM_AUDIO_FB
),
AIOC_HID_DESCRIPTOR(
/* _itfnum */ ITF_NUM_HID,
/* _stridx */ STR_IDX_HIDITF,
/* _boot_protocol */HID_ITF_PROTOCOL_NONE,
/*_report_desc_len*/sizeof(desc_hid_report),
/* _epin */ EPNUM_HID_IN,
/* _epsize */ CFG_TUD_HID_EP_BUFSIZE,
/* _ep_interval */ 0x20
),

AIOC_CDC_DESCRIPTOR(
/* _itfnum */ ITF_NUM_CDC_0,
/* _stridx */ STR_IDX_CDCITF,
/* _ep_notif */ EPNUM_CDC_0_NOTIF,
/* _ep_notif_size */ 8,
/* _epout */ EPNUM_CDC_0_OUT,
/* _epin */ EPNUM_CDC_0_IN,
/* _epsize */ CFG_TUD_CDC_EP_BUFSIZE
),

AIOC_DFU_RT_DESCRIPTOR(
/* _itfnum */ ITF_NUM_DFU_RT,
/* _stridx */ STR_IDX_DFU_RT,
/* Make this as template, due to quirks necessary in feedback endpoint size */
#define CONFIG_DESC(_quirk) \
TUD_CONFIG_DESCRIPTOR( \
/* config_num */ 1, \
/* _itfcount */ ITF_NUM_TOTAL, \
/* _stridx */ 0x00, \
/* _total_len */ CONFIG_TOTAL_LEN, \
/* _attribute */ 0x00, \
/* _power_ma */ 100 \
), \
AIOC_AUDIO_DESCRIPTOR( \
/* _itfnum */ ITF_NUM_AUDIO_CONTROL, \
/* _stridx */ STR_IDX_AUDIOITF, \
/* _nBytesPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE, \
/* _nBitsUsedPerSample */ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE*8, \
/* _epin */ EPNUM_AUDIO_IN, \
/* _epinsize */ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX, \
/* _epout */ EPNUM_AUDIO_OUT, \
/* _epoutsize */ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX, \
/* _epfb */ EPNUM_AUDIO_FB, \
/* epfbsize */ ((_quirk) ? 4 : 3) \
), \
AIOC_HID_DESCRIPTOR( \
/* _itfnum */ ITF_NUM_HID, \
/* _stridx */ STR_IDX_HIDITF, \
/* _boot_protocol */HID_ITF_PROTOCOL_NONE, \
/*_report_desc_len*/sizeof(desc_hid_report), \
/* _epin */ EPNUM_HID_IN, \
/* _epsize */ CFG_TUD_HID_EP_BUFSIZE, \
/* _ep_interval */ 0x20 \
), \
AIOC_CDC_DESCRIPTOR( \
/* _itfnum */ ITF_NUM_CDC_0, \
/* _stridx */ STR_IDX_CDCITF, \
/* _ep_notif */ EPNUM_CDC_0_NOTIF, \
/* _ep_notif_size */ 8, \
/* _epout */ EPNUM_CDC_0_OUT, \
/* _epin */ EPNUM_CDC_0_IN, \
/* _epsize */ CFG_TUD_CDC_EP_BUFSIZE \
), \
AIOC_DFU_RT_DESCRIPTOR( \
/* _itfnum */ ITF_NUM_DFU_RT, \
/* _stridx */ STR_IDX_DFU_RT, \
/* _attr */ DFU_ATTR_WILL_DETACH | \
DFU_ATTR_CAN_UPLOAD | \
DFU_ATTR_CAN_DOWNLOAD,
/* _timeout */ 255, /* not used if WILL_DETACH */
/* _xfer_size */ 2048 /* max size for stm32 dfu bootloader */
DFU_ATTR_CAN_DOWNLOAD, \
/* _timeout */ 255, /* not used if WILL_DETACH */ \
/* _xfer_size */ 2048 /* max size for stm32 dfu bootloader */ \
)

uint8_t const desc_fs_configuration_quirk[] = {
/* quirk is required for Windows, Linux doesn't care.
* (see https://github.com/hathach/tinyusb/pull/2328/commits/6a67bac47c0f83eebd63ca99654ed26e51b21145) */
CONFIG_DESC(/* quirk */1)
};

uint8_t const desc_fs_configuration[] = {
/* no quirk is required for MacOS, Linux doesn't care */
CONFIG_DESC(/* quirk */0)
};

// Invoked when received GET CONFIGURATION DESCRIPTOR
Expand All @@ -185,7 +212,15 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
(void) index; // for multiple configurations

TU_ASSERT(!TUD_OPT_HIGH_SPEED);
return desc_fs_configuration;

quirk_host_os_hint_desc_cb(TUSB_DESC_CONFIGURATION);

/* Try to guess the host OS so we can apply the quirk where needed */
if (USB_DescUAC2Quirk()) {
return desc_fs_configuration_quirk;
} else {
return desc_fs_configuration;
}
}

//--------------------------------------------------------------------+
Expand Down Expand Up @@ -312,5 +347,18 @@ const uint16_t * tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
buffer[0] = len + 2;
buffer[1] = TUSB_DESC_STRING;

quirk_host_os_hint_desc_cb(TUSB_DESC_STRING);

return (void *) buffer;
}

bool USB_DescUAC2Quirk(void)
{
if ( (desc_req_buf[0] == TUSB_DESC_STRING) && (desc_req_buf[1] == TUSB_DESC_BOS || desc_req_buf[1] == TUSB_DESC_CONFIGURATION) ) {
/* This pattern of descriptor requests is only found on MacOS devices. MacOS needs the non-quirked descriptor */
return false;
} else {
/* Other patterns hint to Windows, which requires the quirk. Or Linux, which doesn't care and works either way */
return true;
}
}
8 changes: 6 additions & 2 deletions stm32/aioc-fw/Src/usb_descriptors.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_H_

#include <stdbool.h>

/* Interfaces */
enum USB_DESCRIPTORS_ITF {
ITF_NUM_AUDIO_CONTROL = 0,
Expand Down Expand Up @@ -104,7 +106,7 @@ enum USB_STRING_IDX {
TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN + \
TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)

#define AIOC_AUDIO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epinsize, _epout, _epoutsize, _epfb) \
#define AIOC_AUDIO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epinsize, _epout, _epoutsize, _epfb, _epfbsize) \
/* Standard Interface Association Descriptor (IAD) */ \
TUD_AUDIO_DESC_IAD(_itfnum, AUDIO_NUM_INTERFACES, /*_stridx*/ 0x00), \
/* Audio Control Interface */ \
Expand Down Expand Up @@ -138,7 +140,7 @@ enum USB_STRING_IDX {
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \
TUD_AUDIO_DESC_CS_AS_ISO_EP(AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, AUDIO_CTRL_NONE, AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, 0x0000), \
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */ \
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_epfb, 1), \
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_epfb, _epfbsize, 1), \
/* Microphone Interface */ \
/* Standard AS Interface Descriptor(4.9.1) */ \
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \
Expand Down Expand Up @@ -174,4 +176,6 @@ enum USB_STRING_IDX {

#define AIOC_DFU_RT_DESCRIPTOR TUD_DFU_RT_DESCRIPTOR

bool USB_DescUAC2Quirk(void);

#endif /* USB_DESCRIPTORS_H_ */
Loading

0 comments on commit ce4ed9d

Please sign in to comment.