From 2c43289688627cad1246c618d338321d9a31b813 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 11:31:50 +1100 Subject: [PATCH 01/19] modules: update ChibiOS --- modules/ChibiOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ChibiOS b/modules/ChibiOS index 88b84600b59d2f..c6d0293e32d096 160000 --- a/modules/ChibiOS +++ b/modules/ChibiOS @@ -1 +1 @@ -Subproject commit 88b84600b59d2f39c9d9137aacdc9f098b374b4a +Subproject commit c6d0293e32d096dc88da861a0db40bfd1f6f8d9d From f1ddfcff61b1893710d50400b90926b57d507535 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 11:07:07 +1100 Subject: [PATCH 02/19] AP_HAL_ChibiOS: separate method for memory error on IOMCU FW --- libraries/AP_HAL_ChibiOS/Scheduler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/AP_HAL_ChibiOS/Scheduler.cpp b/libraries/AP_HAL_ChibiOS/Scheduler.cpp index 5d4717eee002fd..a646837658b406 100644 --- a/libraries/AP_HAL_ChibiOS/Scheduler.cpp +++ b/libraries/AP_HAL_ChibiOS/Scheduler.cpp @@ -95,6 +95,14 @@ THD_WORKING_AREA(_monitor_thread_wa, MONITOR_THD_WA_SIZE); #define AP_HAL_CHIBIOS_IN_EXPECTED_DELAY_WHEN_NOT_INITIALISED 1 #endif +#if defined(STM32H7) && defined(IOMCU_FW) +void AP_memory_guard_error(uint16_t error); +void AP_memory_guard_error(uint16_t error) { + // simply fast reboot for now + hal.scheduler->reboot(false); +} +#endif + Scheduler::Scheduler() { } From 4d1722f925bd68f8dcdafbbe8624524ba18f6fe2 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Tue, 28 Jan 2025 12:47:32 +1100 Subject: [PATCH 03/19] AP_HAL_ChibiOS: add support for IOMCU on Secondary --- libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary/hwdef.dat | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary/hwdef.dat b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary/hwdef.dat index f0ea70db249208..d5577719e325d1 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary/hwdef.dat +++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary/hwdef.dat @@ -291,7 +291,16 @@ INT_FLASH_PRIMARY 1 define DEFAULT_SERIAL7_PROTOCOL SerialProtocol_PPP define DEFAULT_SERIAL7_BAUD 8000000 +IOMCU_UART UART7 +define HAL_IOMCU_UART_OPTIONS 8 + define AP_NETWORKING_DEFAULT_STATIC_IP_ADDR "192.168.144.100" define AP_NETWORKING_DEFAULT_STATIC_NETMASK "255.255.255.0" define AP_NETWORKING_DEFAULT_STATIC_GW_ADDR "192.168.144.11" define AP_NETWORKING_BACKEND_PPP 1 + +ROMFS io_firmware.bin Tools/IO_Firmware/iofirmware_cubered.bin +define HAL_IOMCU_FW_FLASH_SIZE (0x200000 - 0x20000 - 0x40000) +define HAL_IOMCU_BOOTLOADER_BAUDRATE 2000000 + +DMA_NOSHARE SPI1* SPI2* SPI4* UART7* From 10f97dee4d34bdc81dd64530e97a3a3e256b432d Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Tue, 28 Jan 2025 12:49:24 +1100 Subject: [PATCH 04/19] AP_HAL_ChibiOS: add support for using hal serial as IOMCU Serial --- libraries/AP_HAL_ChibiOS/Util.cpp | 17 ++++++++--------- .../hwdef/scripts/chibios_hwdef.py | 18 +++++++++++------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/libraries/AP_HAL_ChibiOS/Util.cpp b/libraries/AP_HAL_ChibiOS/Util.cpp index 1012216b023c86..daeaa07b52f5fe 100644 --- a/libraries/AP_HAL_ChibiOS/Util.cpp +++ b/libraries/AP_HAL_ChibiOS/Util.cpp @@ -658,14 +658,17 @@ void Util::uart_info(ExpandingString &str) for (uint8_t i = 0; i < HAL_UART_NUM_SERIAL_PORTS; i++) { auto *uart = hal.serial(i); if (uart) { - str.printf("SERIAL%u ", i); +#if HAL_WITH_IO_MCU + if (i == HAL_UART_IOMCU_IDX) { + str.printf("IOMCU "); + } else +#endif + { + str.printf("SERIAL%u ", i); + } uart->uart_info(str, sys_uart_stats.serial[i], dt_ms); } } -#if HAL_WITH_IO_MCU - str.printf("IOMCU "); - uart_io.uart_info(str, sys_uart_stats.io, dt_ms); -#endif } // Log UART message for each serial port @@ -684,10 +687,6 @@ void Util::uart_log() uart->log_stats(i, log_uart_stats.serial[i], dt_ms); } } -#if HAL_WITH_IO_MCU - // Use magic instance 100 for IOMCU - uart_io.log_stats(100, log_uart_stats.io, dt_ms); -#endif } #endif // HAL_LOGGING_ENABLED #endif // HAL_UART_STATS_ENABLED diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py index fb9cd62a61bdc0..f95e1af5596755 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py @@ -1866,6 +1866,8 @@ def get_extra_bylabel(self, label, name, default=None): def write_UART_config(self, f): '''write UART config defines''' serial_list = self.get_config('SERIAL_ORDER', required=False, aslist=True) + if 'IOMCU_UART' in self.config and self.config['IOMCU_UART'][0] not in serial_list: + serial_list.append(self.config['IOMCU_UART'][0]) if serial_list is None: return while len(serial_list) < 3: # enough ports for CrashCatcher UART discovery @@ -1903,11 +1905,15 @@ def write_UART_config(self, f): self.error("Need io_firmware.bin in ROMFS for IOMCU") self.write_defaulting_define(f, 'HAL_WITH_IO_MCU', 1) - f.write('#define HAL_UART_IOMCU_IDX %u\n' % len(serial_list)) - f.write( - '#define HAL_UART_IO_DRIVER ChibiOS::UARTDriver uart_io(HAL_UART_IOMCU_IDX)\n' - ) - serial_list.append(self.config['IOMCU_UART'][0]) + + if self.config['IOMCU_UART'][0]: + # get index of serial port in serial_list + index = serial_list.index(self.config['IOMCU_UART'][0]) + f.write('#define HAL_UART_IOMCU_IDX %u\n' % int(index)) + f.write( + '#define HAL_UART_IO_DRIVER constexpr ChibiOS::UARTDriver &uart_io = serial%sDriver;\n' % (index) + ) + f.write('#define HAL_HAVE_SERVO_VOLTAGE 1\n') # make the assumption that IO gurantees servo monitoring # all IOMCU capable boards have SBUS out f.write('#define AP_FEATURE_SBUS_OUT 1\n') @@ -2054,8 +2060,6 @@ def get_RTS_alt_function(): #endif ''') num_ports = len(devlist) - if 'IOMCU_UART' in self.config: - num_ports -= 1 if num_ports > 10: self.error("Exceeded max num SERIALs of 10 (%u)" % num_ports) f.write('#define HAL_UART_NUM_SERIAL_PORTS %u\n' % num_ports) From 38272df63e244aeb82ef2401db8a1bbf6948bb78 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 11:10:48 +1100 Subject: [PATCH 05/19] AP_IOMCU: add support for new version of Bootloader --- libraries/AP_IOMCU/fw_uploader.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/AP_IOMCU/fw_uploader.cpp b/libraries/AP_IOMCU/fw_uploader.cpp index 31858b00f117f0..c552c0f83f287a 100644 --- a/libraries/AP_IOMCU/fw_uploader.cpp +++ b/libraries/AP_IOMCU/fw_uploader.cpp @@ -60,13 +60,17 @@ extern const AP_HAL::HAL &hal; +#ifndef HAL_IOMCU_BOOTLOADER_BAUDRATE +#define HAL_IOMCU_BOOTLOADER_BAUDRATE 115200 +#endif + /* upload a firmware to the IOMCU */ bool AP_IOMCU::upload_fw(void) { // set baudrate for bootloader - uart.begin(115200, 256, 256); + uart.begin(HAL_IOMCU_BOOTLOADER_BAUDRATE, 256, 256); bool ret = false; @@ -84,11 +88,11 @@ bool AP_IOMCU::upload_fw(void) return false; } - uint32_t bl_rev; - ret = get_info(INFO_BL_REV, bl_rev); - - if (!ret) { - debug("Err: failed to contact bootloader"); + uint32_t bl_rev, unused; + if (!get_info(INFO_BL_REV, bl_rev) || + !get_info(INFO_BOARD_ID, unused) || + !get_info(INFO_FLASH_SIZE, unused)) { + debug("Err: failed to get bootloader info"); return false; } if (bl_rev > BL_REV) { From 6134d0713bc3679d620781f1cd5c8869169e1310 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 11:11:20 +1100 Subject: [PATCH 06/19] AP_IOMCU: add support for custom uart options and iomcu flash size --- libraries/AP_IOMCU/AP_IOMCU.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/AP_IOMCU/AP_IOMCU.cpp b/libraries/AP_IOMCU/AP_IOMCU.cpp index defd5f1916fb49..b005fa88674261 100644 --- a/libraries/AP_IOMCU/AP_IOMCU.cpp +++ b/libraries/AP_IOMCU/AP_IOMCU.cpp @@ -21,6 +21,7 @@ #include #include #include +#include extern const AP_HAL::HAL &hal; @@ -78,6 +79,9 @@ void AP_IOMCU::init(void) { // uart runs at 1.5MBit uart.begin(1500*1000, 128, 128); +#ifdef HAL_IOMCU_UART_OPTIONS + uart.set_options(HAL_IOMCU_UART_OPTIONS); +#endif uart.set_unbuffered_writes(true); #if IOMCU_DEBUG_ENABLE @@ -1092,10 +1096,14 @@ void AP_IOMCU::send_rc_protocols() /* check ROMFS firmware against CRC on IOMCU, and if incorrect then upload new firmware */ +#ifndef HAL_IOMCU_FW_FLASH_SIZE +#define HAL_IOMCU_FW_FLASH_SIZE (0x10000 - 0x1000) +#endif + bool AP_IOMCU::check_crc(void) { // flash size minus 4k bootloader - const uint32_t flash_size = 0x10000 - 0x1000; + const uint32_t flash_size = HAL_IOMCU_FW_FLASH_SIZE; const char *path = AP_BoardConfig::io_dshot() ? dshot_fw_name : fw_name; fw = AP_ROMFS::find_decompress(path, fw_size); From 4222d19396805b87aba6bd4be2a0322d0374c772 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 31 Jan 2025 13:05:04 +1100 Subject: [PATCH 07/19] AP_IOMCU: add support for running IOFW on CubeRedSecondary --- libraries/AP_IOMCU/fw_uploader.cpp | 13 +- libraries/AP_IOMCU/iofirmware/analog.cpp | 39 +++++ libraries/AP_IOMCU/iofirmware/iofirmware.cpp | 174 +++++++++++++++---- libraries/AP_IOMCU/iofirmware/iofirmware.h | 8 + libraries/AP_IOMCU/iofirmware/ioprotocol.h | 8 +- libraries/AP_IOMCU/iofirmware/rc.cpp | 75 ++++++-- 6 files changed, 265 insertions(+), 52 deletions(-) diff --git a/libraries/AP_IOMCU/fw_uploader.cpp b/libraries/AP_IOMCU/fw_uploader.cpp index c552c0f83f287a..4a2303f1d7e428 100644 --- a/libraries/AP_IOMCU/fw_uploader.cpp +++ b/libraries/AP_IOMCU/fw_uploader.cpp @@ -101,6 +101,14 @@ bool AP_IOMCU::upload_fw(void) } debug("found bootloader revision: %u", unsigned(bl_rev)); + if (bl_rev > 2) { + // verify the CRC of the IO firmware + if (verify_rev3(fw_size)) { + // already up to date, no need to update, just reboot + reboot(); + return true; + } + } ret = erase(); if (!ret) { debug("erase failed"); @@ -285,7 +293,7 @@ bool AP_IOMCU::erase() debug("erase..."); send(PROTO_CHIP_ERASE); send(PROTO_EOC); - return get_sync(10000); + return get_sync(20000); // 20s timeout for erase } /* @@ -421,12 +429,13 @@ bool AP_IOMCU::verify_rev3(uint32_t fw_size_local) /* compare the CRC sum from the IO with the one calculated */ if (sum != crc) { debug("CRC wrong: received: 0x%x, expected: 0x%x", (unsigned)crc, (unsigned)sum); + get_sync(); return false; } crc_is_ok = true; - return true; + return get_sync(); } /* diff --git a/libraries/AP_IOMCU/iofirmware/analog.cpp b/libraries/AP_IOMCU/iofirmware/analog.cpp index 1da6d7f4e461fb..7004330a5c5721 100644 --- a/libraries/AP_IOMCU/iofirmware/analog.cpp +++ b/libraries/AP_IOMCU/iofirmware/analog.cpp @@ -22,6 +22,8 @@ #include "hal.h" #include "analog.h" +#ifdef HAL_DISABLE_ADC_DRIVER + #if HAL_USE_ADC != TRUE #error "HAL_USE_ADC must be set" #endif @@ -157,3 +159,40 @@ OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { OSAL_IRQ_EPILOGUE(); } + +#else +#include + +extern const AP_HAL::HAL& hal; + +AP_HAL::AnalogSource *vrssi; + +void adc_init(void) +{ + vrssi = hal.analogin->channel(HAL_IOMCU_RSSI_ADC_CHANNEL); +} + +/* + unused stub methods + */ +void adc_enable_vrssi(void) {} +void adc_disable_vrssi(void) {} +void adc_sample_channels() {} + +/* + capture VSERVO in mV + */ +uint16_t adc_vservo(void) +{ + return hal.analogin->servorail_voltage() * 1000; +} + +/* + capture VRSSI in mV + */ +uint16_t adc_vrssi(void) +{ + return vrssi->voltage_average() * 1000; +} + +#endif // HAL_DISABLE_ADC_DRIVER diff --git a/libraries/AP_IOMCU/iofirmware/iofirmware.cpp b/libraries/AP_IOMCU/iofirmware/iofirmware.cpp index e3277b764e2957..9ce45f6e78d238 100644 --- a/libraries/AP_IOMCU/iofirmware/iofirmware.cpp +++ b/libraries/AP_IOMCU/iofirmware/iofirmware.cpp @@ -25,6 +25,12 @@ #include "analog.h" #include "rc.h" #include +#if defined(__DCACHE_PRESENT) +#include +#else +#define stm32_cacheBufferInvalidate(addr, size) do {} while(0) +#define stm32_cacheBufferFlush(addr, size) do {} while(0) +#endif extern const AP_HAL::HAL &hal; @@ -38,6 +44,27 @@ static AP_IOMCU_FW iomcu; void setup(); void loop(); +#ifndef HAL_IO_FMU_COMMS +#define HAL_IO_FMU_COMMS UARTD2 +#endif + +#ifndef HAL_IOMCU_APP_SIZE_MAX +#define HAL_IOMCU_APP_SIZE_MAX 0xf000 +#define HAL_IOMCU_APP_LOAD_ADDRESS 0x08001000 +#endif + +#define HAL_IOMCU_MAGIC_ADDRESS ((uint32_t*)(HAL_IOMCU_APP_LOAD_ADDRESS + HAL_IOMCU_APP_SIZE_MAX - 4)) +#define HAL_IOMCU_MAGIC 0x937E638A + +#define DEBUG_ENABLED 0 + +#if defined(IOMCU_DEBUG) && DEBUG_ENABLED +#include "chprintf.h" +#define DEBUG(...) do { chprintf((BaseSequentialStream*)&IOMCU_DEBUG, __VA_ARGS__); } while (0) +#else +#define DEBUG(...) +#endif + #undef CH_DBG_ENABLE_STACK_CHECK #define CH_DBG_ENABLE_STACK_CHECK FALSE @@ -73,9 +100,17 @@ static void setup_rx_dma(hal_uart_driver* uart) dmaStreamDisable(uart->dmarx); dmaStreamSetMemory0(uart->dmarx, &iomcu.rx_io_packet); dmaStreamSetTransactionSize(uart->dmarx, sizeof(iomcu.rx_io_packet)); +#if defined(STM32H7) + dmaStreamSetPeripheral(uart->dmarx, &(uart->usart->RDR)); +#else dmaStreamSetPeripheral(uart->dmarx, &(uart->usart->DR)); +#endif dmaStreamSetMode(uart->dmarx, uart->dmarxmode | STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE +#if defined(STM32H7) + | DMA_SxCR_TRBUFF +#endif + ); dmaStreamEnable(uart->dmarx); uart->usart->CR3 |= USART_CR3_DMAR; } @@ -87,11 +122,23 @@ static void setup_tx_dma(hal_uart_driver* uart) dmaStreamSetMemory0(uart->dmatx, &iomcu.tx_io_packet); dmaStreamSetTransactionSize(uart->dmatx, iomcu.tx_io_packet.get_size()); // starting the UART allocates the peripheral statically, so we need to reinstate it after swapping +#if defined(STM32H7) + dmaStreamSetPeripheral(uart->dmatx, &(uart->usart->TDR)); +#else dmaStreamSetPeripheral(uart->dmatx, &(uart->usart->DR)); +#endif dmaStreamSetMode(uart->dmatx, uart->dmatxmode | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE +#if defined(STM32H7) + | DMA_SxCR_TRBUFF +#endif + ); // enable transmission complete interrupt +#if defined(STM32H7) + uart->usart->ICR |= USART_ICR_TCCF; +#else uart->usart->SR &= ~USART_SR_TC; +#endif uart->usart->CR1 |= USART_CR1_TCIE; dmaStreamEnable(uart->dmatx); @@ -106,8 +153,12 @@ static void dma_rx_end_cb(hal_uart_driver *uart) dmaStreamDisable(uart->dmarx); - iomcu.process_io_packet(); + // reload the rx packet into CPU cache from RAM + stm32_cacheBufferInvalidate(&iomcu.rx_io_packet, sizeof(iomcu.rx_io_packet)); + iomcu.process_io_packet(); + // flush the tx packet from CPU cache to RAM + stm32_cacheBufferFlush(&iomcu.tx_io_packet, sizeof(iomcu.tx_io_packet)); setup_rx_dma(uart); #if AP_HAL_SHARED_DMA_ENABLED @@ -129,9 +180,15 @@ static void dma_tx_end_cb(hal_uart_driver *uart) // DMA stream has already been disabled at this point uart->usart->CR3 &= ~USART_CR3_DMAT; +#if defined(STM32H7) + (void)uart->usart->ICR; + (void)uart->usart->TDR; + (void)uart->usart->TDR; +#else (void)uart->usart->SR; (void)uart->usart->DR; (void)uart->usart->DR; +#endif #ifdef HAL_GPIO_LINE_GPIO108 TOGGLE_PIN_DEBUG(108); @@ -147,8 +204,31 @@ static void dma_tx_end_cb(hal_uart_driver *uart) /* replacement for ChibiOS uart_lld_serve_interrupt() */ static void idle_rx_handler(hal_uart_driver *uart) { +#if defined(STM32H7) + #define USART_SR_REG uart->usart->ISR + #define USART_DR_REG uart->usart->RDR + #define USART_SR_LBD USART_ISR_LBDF + #define USART_SR_ORE USART_ISR_ORE + #define USART_SR_NE USART_ISR_NE + #define USART_SR_FE USART_ISR_FE + #define USART_SR_PE USART_ISR_PE + #define USART_SR_TC USART_ISR_TC + #define USART_SR_IDLE USART_ISR_IDLE + #define CLEAR_ISR_FLAG(flag) uart->usart->ICR = USART_ICR_##flag##CF + #define SEND_BREAK() uart->usart->RQR = USART_RQR_SBKRQ + #define CLEAR_IDLE() uart->usart->ICR = USART_ICR_IDLECF + #define CLEAR_ERRORS() uart->usart->ICR = USART_ICR_ORECF | USART_ICR_NECF | USART_ICR_FECF | USART_ICR_PECF +#else + #define USART_SR_REG uart->usart->SR + #define USART_DR_REG uart->usart->DR + #define CLEAR_ISR_FLAG(flag) uart->usart->SR = ~USART_SR_##flag + #define SEND_BREAK() uart->usart->CR1 = cr1 | USART_CR1_SBK + #define CLEAR_IDLE() (void)uart->usart->DR + #define CLEAR_ERRORS() (void)USART_DR_REG +#endif + volatile uint16_t sr; - sr = uart->usart->SR; /* SR reset step 1.*/ + sr = USART_SR_REG; /* SR reset step 1.*/ uint32_t cr1 = uart->usart->CR1; if (sr & (USART_SR_LBD | USART_SR_ORE | /* overrun error - packet was too big for DMA or DMA was too slow */ @@ -156,13 +236,11 @@ static void idle_rx_handler(hal_uart_driver *uart) USART_SR_FE | USART_SR_PE)) { /* framing error - start/stop bit lost or line break */ - (void)uart->usart->DR; /* SR reset step 2 - clear ORE | FE.*/ - + CLEAR_ERRORS(); /* SR reset step 2 - clear ORE | FE.*/ /* send a line break - this will abort transmission/reception on the other end */ chSysLockFromISR(); - uart->usart->SR = ~USART_SR_LBD; - uart->usart->CR1 = cr1 | USART_CR1_SBK; - + CLEAR_ISR_FLAG(LBD); + SEND_BREAK(); iomcu.reg_status.num_errors++; iomcu.reg_status.err_uart++; @@ -176,7 +254,7 @@ static void idle_rx_handler(hal_uart_driver *uart) if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) { /* TC interrupt cleared and disabled.*/ - uart->usart->SR &= ~USART_SR_TC; + CLEAR_ISR_FLAG(TC); uart->usart->CR1 = cr1 & ~USART_CR1_TCIE; #ifdef HAL_GPIO_LINE_GPIO105 TOGGLE_PIN_DEBUG(105); @@ -187,7 +265,7 @@ static void idle_rx_handler(hal_uart_driver *uart) } if ((sr & USART_SR_IDLE) && (cr1 & USART_CR1_IDLEIE)) { - (void)uart->usart->DR; /* SR reset step 2 - clear IDLE.*/ + CLEAR_IDLE(); /* SR reset step 2 - clear IDLE.*/ /* the DMA size is the maximum packet size, but smaller packets are perfectly possible leading to an IDLE ISR. The data still must be processed. */ @@ -200,6 +278,11 @@ static void idle_rx_handler(hal_uart_driver *uart) using namespace ChibiOS; #if AP_HAL_SHARED_DMA_ENABLED +#ifndef HAL_IO_FMU_COMMS_TX_DMA_STREAM +#define HAL_IO_FMU_COMMS_TX_DMA_STREAM STM32_UART_USART2_TX_DMA_STREAM +#define HAL_IO_FMU_COMMS_TX_IRQ_PRIORITY STM32_UART_USART2_IRQ_PRIORITY +#endif + /* copy of uart_lld_serve_tx_end_irq() from ChibiOS hal_uart_lld that is re-instated upon switching the DMA channel @@ -214,13 +297,13 @@ static void uart_lld_serve_tx_end_irq(hal_uart_driver *uart, uint32_t flags) void AP_IOMCU_FW::tx_dma_allocate(Shared_DMA *ctx) { - hal_uart_driver *uart = &UARTD2; + hal_uart_driver *uart = &HAL_IO_FMU_COMMS; chSysLock(); if (uart->dmatx == nullptr) { - uart->dmatx = dmaStreamAllocI(STM32_UART_USART2_TX_DMA_STREAM, - STM32_UART_USART2_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uart); + uart->dmatx = dmaStreamAllocI(HAL_IO_FMU_COMMS_TX_DMA_STREAM, + HAL_IO_FMU_COMMS_TX_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uart); } chSysUnlock(); } @@ -230,7 +313,7 @@ void AP_IOMCU_FW::tx_dma_allocate(Shared_DMA *ctx) */ void AP_IOMCU_FW::tx_dma_deallocate(Shared_DMA *ctx) { - hal_uart_driver *uart = &UARTD2; + hal_uart_driver *uart = &HAL_IO_FMU_COMMS; chSysLock(); if (uart->dmatx != nullptr) { // defensively make sure the DMA is fully shutdown before swapping @@ -255,28 +338,50 @@ static UARTConfig uart_cfg = { nullptr, // error idle_rx_handler, // global irq nullptr, // idle +#if defined(STM32H7) +0, // timeout +#endif 1500000, //1.5MBit - USART_CR1_IDLEIE, + USART_CR1_IDLEIE +#if defined(STM32H7) + | USART_CR1_FIFOEN +#endif + , 0, 0 }; +#ifdef IOMCU_DEBUG +// usart3 is for SBUS input and output +static const SerialConfig debug_serial_cfg = { + 2000000, // speed + 0, // cr1, enable even parity + 0, // cr2, two stop bits + 0, // cr3 + nullptr, // irq_cb + nullptr, // ctx +}; +#endif + void setup(void) { hal.rcin->init(); hal.rcout->init(); iomcu.init(); - iomcu.calculate_fw_crc(); +#ifdef IOMCU_DEBUG + sdStart(&IOMCU_DEBUG, &debug_serial_cfg); +#endif - uartStart(&UARTD2, &uart_cfg); - uartStartReceive(&UARTD2, sizeof(iomcu.rx_io_packet), &iomcu.rx_io_packet); + iomcu.calculate_fw_crc(); + uartStart(&HAL_IO_FMU_COMMS, &uart_cfg); + uartStartReceive(&HAL_IO_FMU_COMMS, sizeof(iomcu.rx_io_packet), &iomcu.rx_io_packet); #if AP_HAL_SHARED_DMA_ENABLED iomcu.tx_dma_handle->unlock(); #endif // disable the pieces from the UART which will get enabled later chSysLock(); - UARTD2.usart->CR3 &= ~USART_CR3_DMAT; + HAL_IO_FMU_COMMS.usart->CR3 &= ~USART_CR3_DMAT; chSysUnlock(); } @@ -303,7 +408,7 @@ void AP_IOMCU_FW::init() thread_ctx = chThdGetSelfX(); #if AP_HAL_SHARED_DMA_ENABLED - tx_dma_handle = NEW_NOTHROW ChibiOS::Shared_DMA(STM32_UART_USART2_TX_DMA_STREAM, SHARED_DMA_NONE, + tx_dma_handle = NEW_NOTHROW ChibiOS::Shared_DMA(HAL_IO_FMU_COMMS_TX_DMA_STREAM, SHARED_DMA_NONE, FUNCTOR_BIND_MEMBER(&AP_IOMCU_FW::tx_dma_allocate, void, Shared_DMA *), FUNCTOR_BIND_MEMBER(&AP_IOMCU_FW::tx_dma_deallocate, void, Shared_DMA *)); tx_dma_handle->lock(); @@ -311,6 +416,7 @@ void AP_IOMCU_FW::init() tx_dma_deallocate(tx_dma_handle); #endif +#ifdef HAL_GPIO_PIN_IO_HW_DETECT1 if (palReadLine(HAL_GPIO_PIN_IO_HW_DETECT1) == 1 && palReadLine(HAL_GPIO_PIN_IO_HW_DETECT2) == 0) { has_heater = true; } @@ -321,6 +427,9 @@ void AP_IOMCU_FW::init() } else { palSetLineMode(HAL_GPIO_PIN_HEATER, PAL_MODE_OUTPUT_OPENDRAIN); } +#else + has_heater = false; +#endif adc_init(); rcin_serial_init(); @@ -436,7 +545,7 @@ void AP_IOMCU_FW::update() // send a response if required if (mask & IOEVENT_TX_BEGIN) { chSysLock(); - setup_tx_dma(&UARTD2); + setup_tx_dma(&HAL_IO_FMU_COMMS); chSysUnlock(); } #endif @@ -447,7 +556,7 @@ void AP_IOMCU_FW::update() loop_counter++; if (do_reboot && (last_ms > reboot_time)) { - hal.scheduler->reboot(true); + hal.scheduler->reboot(false); while (true) {} } if ((mask & IOEVENT_PWM) || @@ -558,6 +667,7 @@ void AP_IOMCU_FW::pwm_out_update() void AP_IOMCU_FW::heater_update() { +#ifdef HAL_GPIO_PIN_HEATER uint32_t now = last_ms; if (!has_heater) { // use blue LED as heartbeat, run it 4x faster when override active @@ -576,6 +686,7 @@ void AP_IOMCU_FW::heater_update() bool heater_on = (get_random16() < uint32_t(reg_setup.heater_duty_cycle) * 0xFFFFU / 100U); HEATER_SET(heater_on? heater_pwm_polarity : !heater_pwm_polarity); } +#endif } void AP_IOMCU_FW::rcin_update() @@ -906,13 +1017,18 @@ bool AP_IOMCU_FW::handle_code_write() // we need to release the JTAG reset pin to be used as a GPIO, otherwise we can't enable // or disable SBUS out +#ifdef AFIO_MAPR_SWJ_CFG_NOJNTRST AFIO->MAPR = AFIO_MAPR_SWJ_CFG_NOJNTRST; - +#endif adc_disable_vrssi(); +#ifdef HAL_GPIO_PIN_SBUS_OUT_EN palClearLine(HAL_GPIO_PIN_SBUS_OUT_EN); +#endif } else { adc_enable_vrssi(); +#ifdef HAL_GPIO_PIN_SBUS_OUT_EN palSetLine(HAL_GPIO_PIN_SBUS_OUT_EN); +#endif } if (reg_setup.features & P_SETUP_FEATURES_HEATER) { has_heater = true; @@ -1067,13 +1183,11 @@ void AP_IOMCU_FW::schedule_reboot(uint32_t time_ms) void AP_IOMCU_FW::calculate_fw_crc(void) { -#define APP_SIZE_MAX 0xf000 -#define APP_LOAD_ADDRESS 0x08001000 // compute CRC of the current firmware uint32_t sum = 0; - for (unsigned p = 0; p < APP_SIZE_MAX; p += 4) { - uint32_t bytes = *(uint32_t *)(p + APP_LOAD_ADDRESS); + for (unsigned p = 0; p < HAL_IOMCU_APP_SIZE_MAX; p += 4) { + uint32_t bytes = *(uint32_t *)(p + HAL_IOMCU_APP_LOAD_ADDRESS); sum = crc32_small(sum, (const uint8_t *)&bytes, sizeof(bytes)); } diff --git a/libraries/AP_IOMCU/iofirmware/iofirmware.h b/libraries/AP_IOMCU/iofirmware/iofirmware.h index df67719f1ab588..4f4923736283e1 100644 --- a/libraries/AP_IOMCU/iofirmware/iofirmware.h +++ b/libraries/AP_IOMCU/iofirmware/iofirmware.h @@ -185,7 +185,9 @@ class AP_IOMCU_FW { bool update_default_rate; bool update_rcout_freq; bool has_heater; +#ifdef IOMCU_IMU_HEATER_POLARITY const bool heater_pwm_polarity = IOMCU_IMU_HEATER_POLARITY; +#endif uint32_t last_blue_led_ms; uint32_t safety_update_ms; uint32_t safety_button_counter; @@ -203,8 +205,14 @@ class AP_IOMCU_FW { }; // GPIO macros +#ifdef HAL_GPIO_PIN_HEATER #define HEATER_SET(on) palWriteLine(HAL_GPIO_PIN_HEATER, (on)); #define BLUE_TOGGLE() palToggleLine(HAL_GPIO_PIN_HEATER); +#else +#define HEATER_SET(on) +#define BLUE_TOGGLE() +#endif + #define AMBER_SET(on) palWriteLine(HAL_GPIO_PIN_AMBER_LED, !(on)); #define SPEKTRUM_POWER(on) palWriteLine(HAL_GPIO_PIN_SPEKTRUM_PWR_EN, on); #define SPEKTRUM_SET(on) palWriteLine(HAL_GPIO_PIN_SPEKTRUM_OUT, on); diff --git a/libraries/AP_IOMCU/iofirmware/ioprotocol.h b/libraries/AP_IOMCU/iofirmware/ioprotocol.h index cc43005f042a1b..c752c4fab33736 100644 --- a/libraries/AP_IOMCU/iofirmware/ioprotocol.h +++ b/libraries/AP_IOMCU/iofirmware/ioprotocol.h @@ -21,7 +21,13 @@ //#define IOMCU_DEBUG -struct PACKED IOPacket { +struct +#if defined(STM32H7) +__attribute__((aligned(32), packed)) +#else +PACKED +#endif +IOPacket { uint8_t count:6; uint8_t code:2; uint8_t crc; diff --git a/libraries/AP_IOMCU/iofirmware/rc.cpp b/libraries/AP_IOMCU/iofirmware/rc.cpp index d654a910330ce6..da611224afda06 100644 --- a/libraries/AP_IOMCU/iofirmware/rc.cpp +++ b/libraries/AP_IOMCU/iofirmware/rc.cpp @@ -27,27 +27,57 @@ extern const AP_HAL::HAL& hal; +#ifndef HAL_IOMCU_DSM_SERIAL_DRIVER +#define HAL_IOMCU_DSM_SERIAL_DRIVER SD1 +#endif + +#ifndef HAL_IOMCU_RCIN_SERIAL_DRIVER +#define HAL_IOMCU_RCIN_SERIAL_DRIVER SD3 +#endif + +#ifndef HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER +#define HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER SD3 +#endif + +#ifndef HAL_IOMCU_SEPARATE_SBUS_OUT_RCIN +#define HAL_IOMCU_SEPARATE_SBUS_OUT_RCIN 0 +#endif + // usart3 is for SBUS input and output static const SerialConfig sbus_cfg = { 100000, // speed USART_CR1_PCE | USART_CR1_M, // cr1, enable even parity - USART_CR2_STOP_1, // cr2, two stop bits + USART_CR2_STOP_1 // cr2, two stop bits +#if defined(HAL_SERIAL_SBUS_SWAPPED) && HAL_SERIAL_SBUS_SWAPPED + | USART_CR2_SWAP +#endif + , 0, // cr3 nullptr, // irq_cb nullptr, // ctx }; -// listen for parity errors on sd3 input -static event_listener_t sd3_listener; +// listen for parity errors on sbus out input +static event_listener_t serial_rcin_listener; -static uint8_t sd3_config; +static uint8_t sbus_out_config; + +#define STR(x) XSTR(x) +#define XSTR(x) #x + + +#if HAL_IOMCU_SEPARATE_SBUS_OUT_RCIN +static uint8_t serial_rcin_config; +#else +#define serial_rcin_config sbus_out_config +#endif void sbus_out_write(uint16_t *channels, uint8_t nchannels) { - if (sd3_config == 0) { + if (sbus_out_config == 0) { uint8_t buffer[25]; AP_SBusOut::sbus_format_frame(channels, nchannels, buffer); - chnWrite(&SD3, buffer, sizeof(buffer)); + chnWrite(&HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER, buffer, sizeof(buffer)); } } @@ -72,10 +102,15 @@ static enum { */ void AP_IOMCU_FW::rcin_serial_init(void) { - sdStart(&SD1, &dsm_cfg); - sdStart(&SD3, &sbus_cfg); - chEvtRegisterMaskWithFlags(chnGetEventSource(&SD3), - &sd3_listener, + sdStart(&HAL_IOMCU_DSM_SERIAL_DRIVER, &dsm_cfg); + sdStart(&HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER, &sbus_cfg); + +#if HAL_IOMCU_SEPARATE_SBUS_OUT_RCIN + sdStart(&HAL_IOMCU_RCIN_SERIAL_DRIVER, &sbus_cfg); +#endif + + chEvtRegisterMaskWithFlags(chnGetEventSource(&HAL_IOMCU_RCIN_SERIAL_DRIVER), + &serial_rcin_listener, EVENT_MASK(1), SD_PARITY_ERROR); // disable input for SBUS with pulses, we will use the UART for @@ -115,10 +150,10 @@ void AP_IOMCU_FW::rcin_serial_update(void) // read from DSM port if ((rc_state == RC_SEARCHING || rc_state == RC_DSM_PORT) && - (n = chnReadTimeout(&SD1, b, sizeof(b), TIME_IMMEDIATE)) > 0) { + (n = chnReadTimeout(&HAL_IOMCU_DSM_SERIAL_DRIVER, b, sizeof(b), TIME_IMMEDIATE)) > 0) { n = MIN(n, sizeof(b)); // don't mix two 115200 uarts - if (sd3_config == 0) { + if (serial_rcin_config == 0) { rc_stats.num_dsm_bytes += n; for (uint8_t i=0; i 0) { + (n = chnReadTimeout(&HAL_IOMCU_RCIN_SERIAL_DRIVER, b, sizeof(b), TIME_IMMEDIATE)) > 0) { eventflags_t flags; - if (sd3_config == 0 && ((flags = chEvtGetAndClearFlags(&sd3_listener)) & SD_PARITY_ERROR)) { + if (serial_rcin_config == 0 && ((flags = chEvtGetAndClearFlags(&serial_rcin_listener)) & SD_PARITY_ERROR)) { rc_stats.sbus_error = flags; rc_stats.num_sbus_errors++; } else { n = MIN(n, sizeof(b)); rc_stats.num_sbus_bytes += n; for (uint8_t i=0; i 2000 && (sd3_config==1 || !sbus_out_enabled)) { + now - rc_stats.last_good_ms > 2000 && (sbus_out_config==1 || !sbus_out_enabled)) { rc_stats.last_good_ms = now; - sd3_config ^= 1; - sdStop(&SD3); - sdStart(&SD3, sd3_config==0?&sbus_cfg:&dsm_cfg); + sbus_out_config ^= 1; + sdStop(&HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER); + sdStart(&HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER, sbus_out_config==0?&sbus_cfg:&dsm_cfg); } +#endif } /* From e2a939cd699b5c6d740377c3e3556ec612999a9c Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 11:21:20 +1100 Subject: [PATCH 08/19] AP_HAL_ChibiOS: add CubeRedSecondary-IO hwdef.dat --- .../hwdef/CubeRedSecondary-IO/hwdef.dat | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 libraries/AP_HAL_ChibiOS/hwdef/CubeRedSecondary-IO/hwdef.dat diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeRedSecondary-IO/hwdef.dat b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedSecondary-IO/hwdef.dat new file mode 100644 index 00000000000000..dc9c177c7455f6 --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedSecondary-IO/hwdef.dat @@ -0,0 +1,170 @@ +# hw definition file for processing by chibios_hwdef.py +# for H757 + +# MCU class and specific type +MCU STM32H7xx STM32H757xx + +define CORE_CM7 +define SMPS_PWR + +env OPTIMIZE -O3 + +# crystal frequency +OSCILLATOR_HZ 24000000 + +# board ID for firmware load +APJ_BOARD_ID 1070 + +FLASH_SIZE_KB 2048 + +# bootloader is installed at zero offset +FLASH_RESERVE_START_KB 128 + +# the location where the bootloader will put the firmware +# the H743 has 128k sectors +FLASH_BOOTLOADER_LOAD_KB 128 + +define HAL_LED_ON 0 + +define IOMCU_FW TRUE +define AP_FASTBOOT_ENABLED 1 +IOMCU_FW 1 + +define HAL_USE_RTC FALSE +define HAL_NO_UARTDRIVER TRUE +define HAL_LOGGING_ENABLED 0 +define AP_CRASHDUMP_ENABLED 0 +define HAL_ENABLE_SAVE_PERSISTENT_PARAMS 0 + +define HAL_USE_EMPTY_STORAGE 1 +define HAL_STORAGE_SIZE 16384 + +# avoid timer threads to save memory +define HAL_NO_TIMER_THREAD +define HAL_NO_RCOUT_THREAD # also disables LED thread + +# ADC setup +PF11 FMU_SERVORAIL_VCC_SENS ADC1 +PA6 RSSI_IN ADC1 SCALE(2) + +define HAL_IOMCU_RSSI_ADC_CHANNEL 3 + +# CAN config +PB14 GPIOCAN2_TERM OUTPUT LOW +PC12 GPIOCAN1_SHUTDOWN OUTPUT HIGH +PF1 GPIOCAN2_SHUTDOWN OUTPUT HIGH + +define HAL_CHIBIOS_ARCH_FMUV3 1 + +define BOARD_TYPE_DEFAULT 3 + +# RCIN +PC8 TIM8_CH3 TIM8 RCININT PULLDOWN + +# SWD +PA13 JTMS-SWDIO SWD +PA14 JTCK-SWCLK SWD + +# GPIO +PD10 AMBER_LED OUTPUT HIGH OPENDRAIN GPIO(0) +PE15 VDD_BRICK_nVALID INPUT PULLUP +PG0 VDD_BRICK2_nVALID INPUT PULLUP +PG5 VDD_SERVO_FAULT INPUT PULLUP +PG1 PWM_VOLT_SEL OUTPUT HIGH GPIO(3) + +# Control of Spektrum power pin +PB2 SPEKTRUM_PWR_EN OUTPUT HIGH GPIO(73) +define HAL_GPIO_SPEKTRUM_PWR 73 + +# Spektrum Power is Active High +define HAL_SPEKTRUM_PWR_ENABLED 1 + + +# Timer +PA8 TIM1_CH1 TIM1 PWM(1) GPIO(50) +PA9 TIM1_CH2 TIM1 PWM(2) GPIO(51) +PA10 TIM1_CH3 TIM1 PWM(3) GPIO(52) +PA11 TIM1_CH4 TIM1 PWM(4) GPIO(53) +PA5 TIM2_CH1 TIM2 PWM(5) GPIO(54) +PA1 TIM2_CH2 TIM2 PWM(6) GPIO(55) +PB10 TIM2_CH3 TIM2 PWM(7) GPIO(56) +PB11 TIM2_CH4 TIM2 PWM(8) GPIO(57) + +# Correctly set Direction of PWMs +# if UNIDIR is set then BIDIR must be reset +PA7 HP_UNIDIR_ENABLED OUTPUT HIGH GPIO(5) + + +# UART connected to FMU, uses DMA +PE7 UART7_RX UART7 SPEED_VERYLOW +PE8 UART7_TX UART7 SPEED_VERYLOW + +# UART for SBUS out +PC7 USART6_RX USART6 SPEED_HIGH LOW +PC6 USART6_TX USART6 + +# UART for DSM input +# TX side is for IO debug, and is unused +PC10 USART3_TX USART3 SPEED_HIGH +PC11 USART3_RX USART3 SPEED_HIGH + +# UART for debug +PE1 UART8_TX UART8 +PE0 UART8_RX UART8 + +# UART for RCIN +PD1 UART4_TX UART4 + +# USART for future use +PD5 USART2_TX USART2 SPEED_HIGH +PD6 USART2_RX USART2 SPEED_HIGH +PD4 USART2_RTS USART2 SPEED_HIGH +PD3 USART2_CTS USART2 SPEED_HIGH + +# order of UARTs +SERIAL_ORDER EMPTY EMPTY EMPTY EMPTY + +define HAL_USE_UART TRUE +define STM32_UART_USE_UART7 TRUE +define HAL_IO_FMU_COMMS UARTD7 +define HAL_IO_FMU_COMMS_TX_DMA_STREAM STM32_UART_UART7_TX_DMA_STREAM +define HAL_IO_FMU_COMMS_TX_DMA_CHANNEL STM32_UART_UART7_TX_DMA_CHAN +define HAL_IO_FMU_COMMS_TX_IRQ_PRIORITY STM32_UART_UART7_IRQ_PRIORITY +define HAL_IO_FMU_COMMS_RX_DMA_STREAM STM32_UART_UART7_RX_DMA_STREAM +define HAL_IO_FMU_COMMS_RX_DMA_CHANNEL STM32_UART_UART7_RX_DMA_CHAN + +define HAL_USE_SERIAL TRUE +define STM32_SERIAL_USE_UART7 FALSE +define STM32_SERIAL_USE_UART4 TRUE +define STM32_SERIAL_USE_USART3 TRUE +define STM32_SERIAL_USE_USART6 TRUE + +define HAL_IOMCU_RCIN_SERIAL_DRIVER SD4 +define HAL_IOMCU_DSM_SERIAL_DRIVER SD3 +define HAL_IOMCU_SBUS_OUT_SERIAL_DRIVER SD6 +define HAL_GPIO_PIN_SPEKTRUM_OUT PAL_LINE(GPIOC,11U) + + +define AP_NETWORKING_BACKEND_PPP 1 + +# only use pulse input for PPM, other protocols +# are on serial +define HAL_RCIN_PULSE_PPM_ONLY + + +define HAL_DSHOT_ENABLED TRUE +define HAL_IOMCU_SEPARATE_SBUS_OUT_RCIN 1 + + +PC4 SAFETY_INPUT INPUT PULLDOWN +PE3 SAFETY_LED OUTPUT HIGH OPENDRAIN + +define HAL_IOMCU_APP_SIZE_MAX (0x200000 - 0x20000 - 0x40000) +define HAL_IOMCU_APP_LOAD_ADDRESS 0x08020000 + +define IOMCU_DEBUG SD8 +define STM32_SERIAL_USE_UART8 TRUE +define HAL_BL_IOMCU_FW_DETECT 1 + +define AP_HAL_UARTDRIVER_ENABLED 0 +define HAL_SERIAL_SBUS_SWAPPED 1 From c811808184532439633ea5c30d5320dc4c8e60f2 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 17 Jan 2025 16:35:42 +1100 Subject: [PATCH 09/19] AP_HAL_ChibiOS: fix CubeRedPrimary-PPPGW build --- libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary-PPPGW/hwdef.dat | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary-PPPGW/hwdef.dat b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary-PPPGW/hwdef.dat index 0dcf4bba308721..38ddac35e7e7b6 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary-PPPGW/hwdef.dat +++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeRedPrimary-PPPGW/hwdef.dat @@ -6,6 +6,7 @@ undef HAL_WITH_IO_MCU_BIDIR_DSHOT undef COMPASS undef BARO undef DEFAULT_SERIAL7_PROTOCOL +undef IOMCU_UART define AP_ADVANCEDFAILSAFE_ENABLED 0 From 3eb1b6f8c8e7dc8494560c876c8bf3dc7552f660 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Thu, 23 Jan 2025 12:42:13 +1100 Subject: [PATCH 10/19] hwdef: add env variable to set IOMCU Heater feature --- libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py index f95e1af5596755..b0ed40371b4da7 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py @@ -1052,6 +1052,12 @@ def write_mcu_config(self, f): else: self.env_vars['IOMCU_FW'] = 0 + # check if heater pin defined + if 'HEATER' in self.bylabel.keys(): + self.env_vars['IOMCU_FW_WITH_HEATER'] = 1 + else: + self.env_vars['IOMCU_FW_WITH_HEATER'] = 0 + if self.get_config('PERIPH_FW', required=False): self.env_vars['PERIPH_FW'] = self.get_config('PERIPH_FW') else: From 1b06686a1a12c2eec774bdbf9ea89870856ca2dc Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Thu, 23 Jan 2025 12:43:03 +1100 Subject: [PATCH 11/19] AP_IOMCU: remove heater polarity setting for IOMCU without heater --- libraries/AP_IOMCU/iofirmware/wscript | 31 ++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/libraries/AP_IOMCU/iofirmware/wscript b/libraries/AP_IOMCU/iofirmware/wscript index 445b95c0e1ee0e..74e49ffc19cb03 100644 --- a/libraries/AP_IOMCU/iofirmware/wscript +++ b/libraries/AP_IOMCU/iofirmware/wscript @@ -20,16 +20,23 @@ def build(bld): ] ) - bld.ap_program( - program_name='iofirmware_lowpolh', - use='iofirmware_libs', - program_groups=['bin','iofirmware'], - defines=['IOMCU_IMU_HEATER_POLARITY=0'] - ) + if bld.env.IOMCU_FW_WITH_HEATER: + bld.ap_program( + program_name='iofirmware_lowpolh', + use='iofirmware_libs', + program_groups=['bin','iofirmware'], + defines=['IOMCU_IMU_HEATER_POLARITY=0'] + ) - bld.ap_program( - program_name='iofirmware_highpolh', - use='iofirmware_libs', - program_groups=['bin','iofirmware'], - defines=['IOMCU_IMU_HEATER_POLARITY=1'] - ) + bld.ap_program( + program_name='iofirmware_highpolh', + use='iofirmware_libs', + program_groups=['bin','iofirmware'], + defines=['IOMCU_IMU_HEATER_POLARITY=1'] + ) + else: + bld.ap_program( + program_name='iofirmware', + use='iofirmware_libs', + program_groups=['bin','iofirmware'] + ) From bad025d467252f8846cabb6c760d4c7d2c7d0a84 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Thu, 23 Jan 2025 12:43:57 +1100 Subject: [PATCH 12/19] scripts: add cuberedsecondary-io build to IOFirmware build script --- Tools/scripts/build_iofirmware.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tools/scripts/build_iofirmware.py b/Tools/scripts/build_iofirmware.py index c2f67096a82fce..307880ae57c4d1 100755 --- a/Tools/scripts/build_iofirmware.py +++ b/Tools/scripts/build_iofirmware.py @@ -72,3 +72,8 @@ def run_program(cmd_list): 'Tools/IO_Firmware/iofirmware_f103_8MHz_dshot_lowpolh.bin') shutil.copy('build/iomcu-f103-8MHz-dshot/bin/iofirmware_highpolh.bin', 'Tools/IO_Firmware/iofirmware_f103_8MHz_dshot_highpolh.bin') + +run_program(["./waf", "configure", "--board", 'CubeRedSecondary-IO']) +run_program(["./waf", "clean"]) +run_program(["./waf", "iofirmware"]) +shutil.copy('build/CubeRedSecondary-IO/bin/iofirmware.bin', 'Tools/IO_Firmware/iofirmware_cubered.bin') From 40238bda842a59245cce2ec8552c20c7085c2dde Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Thu, 30 Jan 2025 13:54:44 +1100 Subject: [PATCH 13/19] AP_HAL_ChibiOS: use TRBUFF define instead of hardcoding --- libraries/AP_HAL_ChibiOS/UARTDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/AP_HAL_ChibiOS/UARTDriver.cpp b/libraries/AP_HAL_ChibiOS/UARTDriver.cpp index 035444cbeede4d..9bb060c34117b7 100644 --- a/libraries/AP_HAL_ChibiOS/UARTDriver.cpp +++ b/libraries/AP_HAL_ChibiOS/UARTDriver.cpp @@ -545,7 +545,7 @@ void UARTDriver::dma_rx_enable(void) dmamode |= STM32_DMA_CR_CHSEL(sdef.dma_rx_channel_id); dmamode |= STM32_DMA_CR_PL(0); #if defined(STM32H7) - dmamode |= 1<<20; // TRBUFF See 2.3.1 in the H743 errata + dmamode |= DMA_SxCR_TRBUFF; // TRBUFF See 2.3.1 in the H743 errata #endif rx_bounce_idx ^= 1; stm32_cacheBufferInvalidate(rx_bounce_buf[rx_bounce_idx], RX_BOUNCE_BUFSIZE); @@ -901,7 +901,7 @@ void UARTDriver::write_pending_bytes_DMA(uint32_t n) dmamode |= STM32_DMA_CR_CHSEL(sdef.dma_tx_channel_id); dmamode |= STM32_DMA_CR_PL(0); #if defined(STM32H7) - dmamode |= 1<<20; // TRBUFF See 2.3.1 in the H743 errata + dmamode |= DMA_SxCR_TRBUFF; // TRBUFF See 2.3.1 in the H743 errata #endif dmaStreamSetMode(txdma, dmamode | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); From 5e287a6aceb779bca2794bab8128fecf7040b98e Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Thu, 30 Jan 2025 14:30:26 +1100 Subject: [PATCH 14/19] AP_Vehicle: set iomcu serialmanager protocol if iomcu is enabled --- libraries/AP_Vehicle/AP_Vehicle.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/AP_Vehicle/AP_Vehicle.cpp b/libraries/AP_Vehicle/AP_Vehicle.cpp index b4f002d52e6fab..48a80135513868 100644 --- a/libraries/AP_Vehicle/AP_Vehicle.cpp +++ b/libraries/AP_Vehicle/AP_Vehicle.cpp @@ -356,6 +356,13 @@ void AP_Vehicle::setup() #endif #if AP_SERIALMANAGER_ENABLED +#if HAL_WITH_IO_MCU + if (BoardConfig.io_enabled()) { + // If IO is enabled, we need to set the protocol to None + // as the IO MCU UART is handled by the AP_IOMCU class + serial_manager.set_protocol_and_baud(HAL_UART_IOMCU_IDX, AP_SerialManager::SerialProtocol_IOMCU, 0); + } +#endif // initialise serial ports serial_manager.init(); #endif From c18910bb052cebcb31a491c2b39e88cafaa4b6e1 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 31 Jan 2025 14:31:50 +1100 Subject: [PATCH 15/19] AP_IOMCU: ignore call to shutdown if iomcu not initialised fixes watchdog trigger on call to trigger after enabling iomcu from disabled state --- libraries/AP_IOMCU/AP_IOMCU.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/AP_IOMCU/AP_IOMCU.cpp b/libraries/AP_IOMCU/AP_IOMCU.cpp index b005fa88674261..aed2a51fe51975 100644 --- a/libraries/AP_IOMCU/AP_IOMCU.cpp +++ b/libraries/AP_IOMCU/AP_IOMCU.cpp @@ -1196,6 +1196,10 @@ bool AP_IOMCU::healthy(void) */ void AP_IOMCU::shutdown(void) { + if (!initialised) { + // we're not initialised yet, so cannot shutdown + return; + } do_shutdown = true; while (!done_shutdown) { hal.scheduler->delay(1); From 78effbf2bd1b81468b4143378ba226a03b7c6e6f Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 31 Jan 2025 14:32:57 +1100 Subject: [PATCH 16/19] AP_SerialManager: add support for SerialProtocol_IOMCU --- libraries/AP_SerialManager/AP_SerialManager.cpp | 4 +++- libraries/AP_SerialManager/AP_SerialManager.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/AP_SerialManager/AP_SerialManager.cpp b/libraries/AP_SerialManager/AP_SerialManager.cpp index 6f9f1541832c8b..5f267784b26668 100644 --- a/libraries/AP_SerialManager/AP_SerialManager.cpp +++ b/libraries/AP_SerialManager/AP_SerialManager.cpp @@ -585,7 +585,9 @@ void AP_SerialManager::init() AP_SERIALMANAGER_PPP_BUFSIZE_TX); break; #endif - + case SerialProtocol_IOMCU: + // nothing to do, AP_IOMCU handles this + break; default: uart->begin(state[i].baudrate()); } diff --git a/libraries/AP_SerialManager/AP_SerialManager.h b/libraries/AP_SerialManager/AP_SerialManager.h index e093e75825e2d5..1be91ba2f9bed3 100644 --- a/libraries/AP_SerialManager/AP_SerialManager.h +++ b/libraries/AP_SerialManager/AP_SerialManager.h @@ -86,6 +86,7 @@ class AP_SerialManager { // Reserving Serial Protocol 47 for SerialProtocol_IQ SerialProtocol_PPP = 48, SerialProtocol_IBUS_Telem = 49, // i-BUS telemetry data, ie via sensor port of FS-iA6B + SerialProtocol_IOMCU = 50, // IOMCU SerialProtocol_NumProtocols // must be the last value }; From 86414c1f0b750a4a7b845da5272ec7f14a6c142c Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Fri, 31 Jan 2025 14:33:27 +1100 Subject: [PATCH 17/19] AP_OSD: add option for IOMCU SerialProtocol --- libraries/AP_OSD/AP_OSD_ParamSetting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/AP_OSD/AP_OSD_ParamSetting.cpp b/libraries/AP_OSD/AP_OSD_ParamSetting.cpp index 7ec03159e4c599..985c1fd3762e0b 100644 --- a/libraries/AP_OSD/AP_OSD_ParamSetting.cpp +++ b/libraries/AP_OSD/AP_OSD_ParamSetting.cpp @@ -139,7 +139,7 @@ static const char* SERIAL_PROTOCOL_VALUES[] = { "FSKY_TX", "LID360", "", "BEACN", "VOLZ", "SBUS", "ESC_TLM", "DEV_TLM", "OPTFLW", "RBTSRV", "NMEA", "WNDVNE", "SLCAN", "RCIN", "MGSQRT", "LTM", "RUNCAM", "HOT_TLM", "SCRIPT", "CRSF", "GEN", "WNCH", "MSP", "DJI", "AIRSPD", "ADSB", "AHRS", "AUDIO", "FETTEC", "TORQ", - "AIS", "CD_ESC", "MSP_DP", "MAV_HL", "TRAMP", "DDS", "IMUOUT", "IQ", "PPP", "IBUS_TLM" + "AIS", "CD_ESC", "MSP_DP", "MAV_HL", "TRAMP", "DDS", "IMUOUT", "IQ", "PPP", "IBUS_TLM", "IOMCU" }; static_assert(AP_SerialManager::SerialProtocol_NumProtocols == ARRAY_SIZE(SERIAL_PROTOCOL_VALUES), "Wrong size SerialProtocol_NumProtocols"); From d67d2ce51665957172c285b67144bf89b1d82446 Mon Sep 17 00:00:00 2001 From: bugobliterator Date: Wed, 29 Jan 2025 14:50:19 +1100 Subject: [PATCH 18/19] IO_Firmware: add iofirmware_cubered --- Tools/IO_Firmware/iofirmware_cubered.bin | Bin 0 -> 98856 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 Tools/IO_Firmware/iofirmware_cubered.bin diff --git a/Tools/IO_Firmware/iofirmware_cubered.bin b/Tools/IO_Firmware/iofirmware_cubered.bin new file mode 100755 index 0000000000000000000000000000000000000000..20bbfd153fb9f48bc98e058fe67a7815af04271f GIT binary patch literal 98856 zcmc${3s_Xu`aiz*-ZR4hBMcV}0d+23fKeO3in4MTU~^L;wT@|Z2CdT;yP$baW*3Kx zg_jD2JO+R}iND@qzD1d=X$a!ZpJE05@jva-I>G(cf#3 zUIh0v+>Br08V&bre>z}q{eOV}b^ia2@HM~qc~Nu@Jc4V4`vGpuc~*Tr+&yqx;r=&h z8!2-jj#W1outsx!p9*$FaeP+8jsq}fPr8mBmy?J@&(%3zY{X>$d9@3^}c5|6zD5(E68Cszr zUSFzR3@LpL&_ShlXR>LQZk6Rk9GN3rkndjPUWsj6uN$-HYBDM&0FuIp`GUK=tu=BrjvrWE#(|VC4i@44HvPZ+9 zCfTUNFf&!BX&CB18#ELZVd-Yl!-hDq(yB&X7>4uu(%802VEw|L!3U!rfE0sX4_uPBE28j)%+e$n8MP{Szd5|=G6u5w-^S4F3j!bG`hi{K4wsL zHa=s3ax#~|-!L>F=`@s*;Gr0q0`*WBC}0Owy`eG{YOn7GyI5(j1dle@U-v>{Wcq0t z5QF`+q?4hr;b&x87zR9@bzj>vS;ZY}T2ZE@TW#4n#MZp*31eQie$Kzj-*#l7^Uj9- z9yMEpIA$=Y-vBzimPr}l)haU8&sVfP*KVOLuOH;jZnZEOBOKfB=fSn1nv8B$21-8v zl2tLt_EAo797<2Sy6q+F43xe+vo3BU+P?pM(9pP4^ptefP}tec0q&xwq>XFaY>n<< zZ!XF_9LskTKFvLoPvDhq75~$z7+yM+pY=vtlZRE`?a_oWX}jG#-|g1%HEs*)iz5nAz#AHKR;{L zKY9tHwzZrsc(gU`~G)?N`QmA71(f{{*s=+TDEKYwE&k4k4h8(m3rMT!sjqQo|- zK)lsFDK{=}tM$f~bV(69=$?=pGb$8|w7C3Q>&-2H|gw-C(5!~jQFwfYP>4l!k>acVoKrt!MTez*88 zVm{8Y>b_A9RdXWSXqj;LVO?~5r zZbtcJH>27xn0;>d;8W+G9Lzrd%HUJypK0oGi62tDtQf?mH286rcQag6gXAK*l58do zEjmNhat+U=1*MG@neZSNV;3XJxeAv?(qV3V%u*0ElrupAi(>Y>Yrn)gGKStn$PT>FkjPgHVX?svOTCajWdm$~a zR4^adEL5y8iWbfwY_>%SMsX4sg>Prjg`@WfHyI~Cy4zy11?MYP1m_qtER&r!#ygoT zvQ^nARE7v%*)|Kt4D*MZZ6(=Sg_c-Q&c`c4geXDVNU0GBnDy15#?DpS%7xhC+Tyz6QuGaFzFL1o z+QDkYr-uE#h4PV>X*OybY41_b{b@y9gFj8%Y~+=B8hxlPTzo_##^Xg4Lff3PgqYt5 zX4Ss_6D?_tX1Uiz%=4`Hso_mwq*Tv}Sk%GKk1o_FT8I`I#gI$==uO5vq1+N>^Q?#y zUa*~B5iZ_2@N<#B#55VVum5OENn>`NlFy!7S)ZNXD!pgnb0dYrLaX$lE=I`A%gX;$ zY9-OSPo*7^Uv()KeAT5|$cR}N6ui)&-s>|&b#a8*TYj1&-*qXHbv)S`s#l0jl8*mW zy2Gjvk4k>3XU#tgsvVc8K0AtP?%7RTfn(VwfWN|(C6 zq`4|PRj@m}kdQmFNy0g2jWplPn!K)5VR11h#NEFmn=x*XPM9ey#)X#Lp&%iIKz3B2 zJ#GnM)o;Mjwjm03G40#AXrnB+l13#zb-q)&ks*_hNtya%k|p9(X)?Y!$;YHc`cI{J z=2Jp(pku_iM`|~O1oP|;1fk9$?BVOUPAe?T_g+%@15XUn*8cJ)X zWksPZaa38KyJ@TnvG0wFZI)xlA@BBxJLbmnwdM5+{&N!%tL=i(Si#b2u%+N9;t$)tj3S!{2KEyU83# zQ<-{Q2PqUw&PA8#CoCYulR=`ytic0PVPtYcYkr*E-;PLC<{jBJ(t^n68$QUZk#0|k z6v*rvY4L)>;+Pi3$O>O|k%etiec@4QQA&-p(C~UTG3t1Q-XdA%Frs^Kgg2Cm6qATj z<4kATnOzGcqexmh!&&uixZAa?Iu34p1gpLY-=eT9-A{qIgMshv-+11G-}Vk;w8MXkdr5w2sUl*hv_;=4RVD|?)H=+ZyR4Ei?vyHY zcIkyH!mpuM5;*k00Q(qw124gEN9v&y{ZFk zM$ne>Z=_`PhAywQuJ7BqZ(^V)cHReh2s%%g|1m1PYTvj(x?eKV^p8$eNhb(NSl?H= zZ&zi;c|M9_YT0);q_MAm$?(2Y{&rx&**ErO35?i2to*}nV5Rg|%Jjc*%CA@KKll0i z_2-{>$Ip@VeUH3T(D(Uj%9V%QzW&dK?r@V9sWE&0*HXKgN&Sreu4Tkl&ThKN$%u0p zV#tPUVw0^U^T^(YM9d3!4ZLTK^Y<=xT++Dqx?qcwF(tbwZR3VX!UCt=Xe^PM6?#U@ z8F$Q<&ubX2!pa+{eHTK-YplkiK;{@Bx_VowKR?fUJ~ zmGOF%MoV9MktDRb?4wFhk1C;S6s|7wTkg?tebQx0x<|ovn|FXeRIi}=fr6)cLzz6w zTBt@cYL+dPbsefN`85NJzTBdn7iCM}BgF|)0;rqov*?AGNHGDkx352INTJh;!vng8 z=cJ#(`3?A0V8)_4%>+njT2k7GP6Iutyx!6q9--imtf+ImEvd|7g=YMYw~leq?_w2m zY=tW2DD{5>zei;}O666!F>0A#L%?jk8XD9x)=)0g7ER1Q0!}k22k~1idEhI@@8z2L z`p*Su_@{ffpf%|D*kxRO^J%li*T1f1mtfIPfvy;_%&gxfMA>#-(mQtlrjFJ);IGd+ z0bZ6gs`#?{khw$`GEZT^3S;&d56dvsjK~kXWj*KOG_OkA$*a;1Gwat8u(lv1UY*t$ zGdE_QQHY_lH}qShFm5iD9@I~HnmZJg8Yo@p$0n8!%?1qRwVjJ%y#;fKX0&b`KbH%{Xqwq1Q zAto9#!4)x&F_;9%4V}yj=vN1j{{#1Es1R4sJU|eWJSS2~L}A`=NAS$NSKUPI*RzP0 zT`V?ML7tb_S4ndX3cl7l)k&s2SrBo2aWQiZI1SBcwxIIf!!U;S6^9-Eb&PsO4f2FL zJ(eCv6jn#c{A}Tr%b$a;L;UH#xGJQ%DF-Bn`G9mlS8JUN%q0cPaT?>XbUB!7Xx`Z} zh0!gYa#`vbzqpW2h+($Cys<|*K$vZ`mPa7@p-0wLNT;C1oCL;-hy&6y$;sUuSK>S% zJ$Z`)yENtY#CG;3X2zrKx5+%BIhTSSlTaf)2|bSH!b<5u%8h6AG-MJ&GWNhv49pw9 zT@qI>HYFBI#h2tReNgS!7L#UxylJEA||DeKQ8pGRS}c|2B8|zw1wG&7#5*Th_BJ_k+wy=;|63hAobN5mEg}7_-o)VgMShGQa*!>z^uM-YQArZ!iAbjue}^bTltg1z<;C@^gp6k5HK! z;Ld~YD%8&I_zTAKh<=X#sw3xKdI&ness#=EC0*-oiL|n*o2~uOUvpBTno6=`?dRP@ z%z3}0v2dP(H_>q{MhwVbp$_Gcgg>~b)^Svd(bvU(ZkpR(gPC|~gXuCwT7L`DdGrZr zYmcWalvAJ7`uV;Ee5YxTBP|EcW-#rq8OVeVGUK}%$*bKMlz2>Qn)WR-+tPciXsJU1 z4Q9X8M=Gr~EyTzetF0B14Lunb8LQ_dnpR1%9*vz)G>0AZA zd)>55n$ioc=`wsl3nPXOnFJM&n#wD3nyW$*iPn%IvYf)(2aVlp_H%%)I%w_9{2?hO zIdyG?)N5w2??LnIk)oOH(p=rLvqUa&wVU$2q>)Sxov-Ad!{}3BO#Wm!?C1;%K9(vx zC*@;&{%GL@7AqS%YMEK;>D{2)pYH|mAAMj|7*@m%-!bW1vx;X@KgHMHa7;RmJ!B2b zq$-j=g@2M$ouqe-NY2Lx%PX4Qe_F8~GEU)(9H>mi|phMD^y0Bw3*V2ZgUC*a@VymsHBU(LHL?wr$yB84g z+~84Y)E@Nkxb~a#F*7eaBpp~#TXa;~iP#j=#P*|7P0HEW{pZ%LH?|#>st_AuGPSGG zTE9TYnS*vbDjkqxv)gCp7l`&{mvbLS&pjmVT;S_(0(~QRI*-uUnq|Iz%P{s4#D37= z$9qJEuLAtj7iga!zA!eRk=8vxS59xGY;L0QpAXaW-$D5;Xz4pxMjjQMxTcBLbrsh8 zL@XZa!q?X?UG%RU!o{yH(z?Cnrt>N7dvtEK!(WI!BBEgzGdbYnMyKv%dCNp*wIC1)O&t+bH`S02M%#S~Ay87zOO#J-z-?hK`1JpYFgL^jrABW{U z&*l?TGu1>qILOpRHME{v_fU)jL-2(K zxbUc(Vjo?%PXcwTLVijWtqG;;VbDcPjZQyh4epR(pYO=LW!n3Ce7=>5WozjAyk44W zsFG#|`l-IHUYdwrI>Grr_ffUE@)S6trBo~!;V{>B)hC{xjG^H0Ja?@%uj5^59PseK z6ZxyY{*Bu|KBi-QB+KPiTMdAp%$8Xv{+DvY%r&QKt&;#51I%&1fqChvYHLK>yHY$b zjrooA4$Q-KaM`Db@re6fDQRRpMb=ui$Y%gv&2LaZ64&?5-8UDdYf*Y^0P5HEqEOfG zyB@fyZ`Qt9rvMv?6Tu@(V;98I*_637-i@b7zW!9q1)q;JZmehCWW_%ZDRRa`tMuzH z!S#1aiXl7J7HN%9^P+O2=SE}Qa_>MQPuDNY@umB&zaL-?+FYGcHz8a+Hb}&i!x@zs zqJqn?6Y+c5ua^BC)Zb@co?%aG))sSW3G0@4;C?R?vKC)$K*nvT|6ZD2%84z5DvYI% zhLt$IBx2tXNw@|pj7o`^=j3P&D|k+bFD6FF-h}&{-%EW}+Jpj;C<;U-B%wvC>i`^= z8N?r^-{Z`z6OIq9i`x`?3;KRYN#Ko{QIa;Eh)-Q038FI@E3ddh+#&3Or$cA)C z3Nt8}Haqh$d?RIFg|e5RoRO0AP_lPa$rUKM%f-y1Wm8(+NBk0sQqk;I=GO}CHYTOk zcGgY#Mfpbgl>j#xZs(H>nb9R>(48zLk5!$pEP+hpnBM8#l*b;!wyWH5MYLILzIk*q)tffM@&cFHMd2Ne9@ zq?jsB?2%)bn>n#-05Nt2KThawTq;qhv%1Mo6#Q+ z^jJc?J=(#ruMhMbmwl?E2YNo1eaXWF=VSFfHvE?Kl!m<9Lqc>tcKOV#(#jpFvZBR# zlmmxwXO;E!OJQwe$lToC6=A`-nZ1Sg8K5~%1pZzRB9^xYijVF>@AMM(XrWC+f&8c66eQufADI$}fe!xFU4;iqPM#2z`1* z=-@Yt3FTXcqt(%h6LC9EWkVGckLHq%4z)smG>6z7jAAm(E9?$dF-`t*iX{2VC}srW z&4Kt-`CF2m-mB-zyVDtphu)*82D&61*YW!*$)cl5dZJ(Z4&w3VE?w1K(a zI*}L)2TQ$?5)&SSenUK3-9bmc>!8<%zFU&*!+CGHy1cuP%dnJpJHaV!6hd%SQFI3i zaa!8%spI&D!BVlK)Dm2h-MSIuG7wL{QX`of#49EcuSJQSyE!p@Ttl3^t1{I__wLeo zsW}a2V0Y_${Xbk(^122+uMlq=yglD!d&O3P*sl@0SdPsfOyC#gQ@Qy=GEQ4&2Z@o= zNt+!ohvb~|^BIGlSBjm|l%lr~+jJ@COytbDl(TBKN<8O#rRa6QyapIS#vVUN3>K`e zen?h&w~!6e%{r4!CH~+$S@a&@ssZ<&45tRJ>+%x}1SdYi>qbd8*Vg}FjJLYbx;GpW@2$=Ff4q2w489z6Q;JH2acu* zBw?j9>47M#;N*mKehKDR?am{Ty0u&&)yK217eZGo%v#bBHa!&ctwL0XsMYF_kP!OC zFJVn{J2ngC`8S3A9wu~pNlXiLlu%GmAA*zrs3MOL+CU7kMWz-0@5;b;Z{X_*d`-oH z^uTv-;Ohx|O)CTGf$!eH*Aw`f?(?T(2dUPy>dipynQIl-X=j$X{Ja?9@m9VPJRZK7 zM0uQK1dkWSCkA*NY8?+A6P$FuX0MJ<=ko%5?!1D}pGa7>h|kzZc)jtspU+oM*vmWk zkU`bC-${ypu9=vJcCztVBBO-X<0av1ot=`+Ji)rg`K_cje=EUOr~2QL20m@!j_ezR zs1-fg;`4;RNy=2|!2y^QA`fSbd+LcjqYCKXS^L2q# z(e*>A-I$1-i&$6-mRMME$44t3p>{1s;i#Jl_`bjnNmcey`S-Z{kD%ibxclLX;Ha*t zL=QQ0!Ooj7UOCsB_myJ2rne_OLXvKTO~Rt8CbZ_g&N*|#P0vYpP#e(TMN*3GA!ow< zSY2SR&zBIyC$#vc*&E1Y8b^1h89oI%GD$q_U^;#M?+sJE{Wa?^?G)Dx=id8Yht(Y2 zZwl=IPMk+|Iuxc#?1^qbi2g1zQ~hwH^r_BC`GC_dl#*>E=@ZxV2}-Xeh|UMed<-9j z8G-IOzJ~eWfScChxYI8sunUTKlF;tDT;f05U2crqV8$2%`;$dxR($Zn>-PXVNxWa- zX(hgXlh3r4)->Ei>$w>mIpj{epF{g`_pA8O24DZ(zF8Agmu4hdKCSax*7Gi6+%9F3 zC($!YnyLJwc2p(VY-%BuVB*vk8b>hij>PC&F#jP=35o$@-al{yfu$7eYD==OjKA?x zJm$Mm^C2yLX`uA9W>^?JFg|m$h044rYjg{vqvewpbz^XTtk4$398>4O*jEhKsB##+ zD$kBx^YJ!iZiHAez#!#DtU)Yrf|-~fNB<;S|c?O_xzvQxn!f|5Ra7a$1-pGgKWk@%N z(ppzYpK2q7LpX0aB#mPtuoD<<(>8NCtXMU)18qRdu*q%Vl;Up*&}A6AoYht%x`BTPJR@_H{lK!=@zj?Ij5~3!pMCAuwvjHfIPE z?k6)ku+Is(nXOsdMh=}#j~;u2!W*L6Hgx`MVqT|%^;EDirdq!33@J{4WH-U}gWzLDlekJ=yDs zCsXl$R-41!Gj-jC9{0Mdda9P}kv=Q@tR2$kDLeSG7LJgIGt(MFAJOnizPc$CeR-_@ z<+ZO!F%h**$)@S;KI1FWxa8%Jgqadr!Z6lilhc~>M?sM!k8}6h`W{6{L0?MAU(UqG z2K7|k@co&%5D8^Y!)ggS#K3#QmXPLvRbZThVm#(vYhpX#(>vWX$6wBb7k_^ye92#X z$U2(y`!m7C5BGew?x~(v@a5Ot+wlOtIPEn(01{cV@PoAE|->bZ_iVs z%Gm>IX|B(((>_NRV^nxyonA$El6}~jk7pQT{h(c}kfM_O_@?uZxI-*01f)69~Q9I7{ z86p2N-blGoB^@%)F-5hrrtl6kcyhB&wq{CF;_PCMZVY<)jftubzx~pPuC+D(uw%28 zX=8L}VH0K7GB=EVG_)Js%GnB=5~!5`Wl1wyIH^T^A`o(tT^O z-+o9(q1hYSV%r=Q2z}DT=nhGWTMkK5;oopNI4*)158xC{#|JwONf!zu9pFnOYCv&!qoV74rjX;Xo=p!U>;(X`F%2w*Fse4^AHTLhX4=gbIc zXJ@2!)JQj*KQ}2r=Nw)2#2ZcHWlDxvYaLI49 z_WMm}>mszZt^GgS+V<T=Uu73P!f z#qpFHP?Gqb>qDe3(~?B1YXUUD5IvRfIC~vJ9i^g4Z1Q2paR2j=8h>?JdRHdHP7SN* zbq^`L%L$~HUh$?DQt)w{hp)qoG9fUd#ILK79x>C|?BLl}j?OH7J%3wAcN?c1qcieX zXSj7QN*b+v2WczC!N{dvH(23j5*e2!fpbnsnA7*fJI}f`Q*=D2dX5u+rW({GnhJam zxPAKX+^MO%EmZ!~{f)7p+%piIkDh&L|3ZngbOP(M3aJwO+6ew_gGDWAX@w48#+b94 zpT?Zo3K{W*+-75rfYV8KaMli9QBK<>8?5*IoPHvy3ceeh#MJMsk}AmQ2FiD6HkvX~ z$AY`9quLZ!xHc{#(5By`O~3n9n?9c8Z?_XywA;^e4tJp4PP#q`wA)Aib}K}?&G^*y zciL{~`59=p23H7AszNZcaN_M(aOp$X{vG-+Ew>Xg`u~fTTQacKtw~^9uF@XTPx8rnhrB;Vb>-pyCm^9OkIfRqRmo#B1R6^vwB^dc$c$`*wUN0R zqoferej0YEjMF#ccGr@4V`hc)q>heAEwyvQh0TGhhoUdg z-bc1`@;tl+`d1NQuBW5f0c(L1zv>gq0;9P`s)8o+9A?Qa7|+v+jhTNrGj(0HbQdfV z?=(YSOrj&2j_lE+`kLPy)fAV1T<1$~xnuNG4V2O-jL{&CNWzxY(Y8u#x)H>;4g7N#|y4vh6m z^HzDRR|dxVIE-~Wc(+S#U%jj|Z`I*%Fa7cVy*T30>B_4BOH6K9){cQT`Lq_Y%IQlQ z=ephBYqV$`T?FLuo$*}e83!;I(wHjn^2L$%n$Z(zFA4`7Xs;(+1TBRT{9Gtx=_8c) zqgtyICub-BOKY*>M?QaRrO2(7@_%iux&O7bSaFT-ZTD0?F~;b?eXYN4RRn!ZuHQ4X zWmd^86G-#Nm$~DRNBf(3)}XbCQT>}~e{kPEvB*sO+#|K6;vi+9-wJdb}*teq{xSqfX%}W>-0yqhD&Q4!&gaLbsHa1 z@SaC@N;TSDSfi@3M%{_o%!FB1!Bt4xF|VyPP3u6MZgkw($*}t>*WGTX2tHS$maaJzgcH zYr|lRo8Hap8BZy*QQL6tUu%fB%3dX9==NwM8O6DW*I2tJM(&jEvap^qcAM65?q5%= zJ6a`W;pXhVuqr82s{n?ua}Pg3vFwy=$1h=eVj19g9fgZzD1@~;8km+Hzl3SkyrUbn z%(;hG?Ue4;e&{uZM?orR#0?uZG)4ZGzgus;wTT#TI)pKufe~-DL^7aAt`98(6e+f8 z)4LUV_T0m(H{zBBl?%L%mK4dLE?QbA)I4mPmO`L(q-W}^-C>52m}RGda&G?n>t7lh9*;>X>N0@W{t2!N9EwDII>_G|7$H3+j)`*I8 z{jfqwXKE}jgNM(@S4p`RF4Z58vqm|ETB*=f*d1%NJjJvX)?9aKwawv|@uPh@9n85q zR*jVK0xdy%!a?~>cVTE6E%^mnhL%gcGNltM_`9^Uq(CXiMRWMe@$V*-4g0xDTC%Y6 z6){ff+EVG{&Z`#IS{bLjZWH66e=dQwBwb~0(OG-7tLO@NK2HA`;~{B_4QuwG3P3bt zmAOR6CjBI@Fw0EeySSuutO4g}H^({>(Lr%sn!4;VM!r&1{G*;r>#Zs-<<<98Y0hg{-8qh3;OC$hx=u@+`3Lc(MN+?CztH+VU&iFp^=*i&^S;o$YI(Qhkj>#{ z^cLKGl-$XHtjeZvJ#Iw~Blb)5w@;#L*~c?j^?i`HIdCKEJE}vL$vWg|aQ_4JvV3Tb zW1OsMEPBp|SWkQ)--vj1#t^k0e`t^NzL_;J`U>gh!YJq;&uA-fuhfGUenxjoydm?j zqYNo*B2D?RgG&qomiKjAt-QSJ6&BcIBuq8 zCi1kUnG1zYjEEg>?Cw+%O7Vwioo960+y@3el2>RoQnOj%t&w-}BB58Z;_{)BZVp_# zU829YNwR%gfNyjzWyJ*8d&qO#dEcrETm+(X+#czpE9STxB-$tIk#7rJIP6=?8?=o} zh4}mLXrOnKoC8K7^io>}qb(Vi7FI2EFN0c;#$*#vL(g%3!8o9}M%wVIe7ZwDNtm<$ zQDB?A--y$nFsu$ci+5z7ZZP7+Ovl&ZPKKS|h;kH?rPuXPy+=)oJi*Kk>nbnd*du>N zpPUWYB-mr=_)YH+*rY`!!8Xyz^*)t~`P>}pWRnygE5WNav(L0w z-MKWFNl|z#q|06MVM+G*eBjkqqYPs5V~-kzJt|kmJ~w=xAG_v1uz!06_QFxv{dJkN z!E?(Vw84kT#C#C0qMp&A&wDkREvLf>TTfpnq4lNbrQt?0Rhy{c$Jt=>Gx;{#cH7ss zaYeTkjr&tn2}xYya8 zh`z;97tEyAIWll3HiM5iehqFyPMo_ii$agU?HS@SMngs7G=>{w&S%Dd>{W-(?N+IX zmkndQoF-#1bZFe7kG)z(d*_#D52-@ao2~iXglKQQZLk7%3}M>OJp8y;M+V*z(0D#m zu~L{>g*OHcy3nrAk`~&JX+7%)=E{3N^#k+eul@d#>?)jsMKx4m6RRXU1~wkFpAYw+ z8ob!Qz{eVtxeD*ygov{SL)9F$zzX;$8#uIuY}Hdn)PoK1gz!O3$f=``;KG z?tgla!TSRgComS4*T3DM;0f{;%K2vcUMV)*PmDhlGq3Mj_PkZy@|^XjWjO7cf*DQB z@+HXFN@!k7S^WzQkx5F75}H4uB@!5-`G*Ze4D}^WSVqjSd|Y;cQjrKM=nXK+4-WT7 z{PJ-aXG==VQa1CW4d8@|!>cwV@?!rBKIY98ycrgaUWubSd$=K-0KWY^Ky~&Kxu$Q) z)c$Ljh}R8M7~?&|{e1(G2@0=*0cUidI1(V0%$Oa|4$!=V0~BhHG}k=bPv}kH*Y`;C z!QF3%WsHe*w$eamEBTr2(_ptj{nZ2{B@z2l;cZweD!dvFG3-Kw zRA$Txgb|GcT!N}YVW1)T2Ap{90C21_;m(7iFK)%G45L-$V(G~>Y{AKG3Zu(?vw;2V zYO?@Yg4{vvNjSGRb}JOoMSeJi!7TW3X$LN+iEhP%umOSGN^7L2gp4s-XRsMpUF;9P zKoJN89L}O!G*MxR*}t6*)B|n|}H3CzIP_^Iy6nm5xgqPV|ErU zM|^PmkN)_iHO^q6j{JQwlk)dM#<+Wp)a<9Bz5PZ|>ZjsE7sd4zO3H|ATanj*-DudS zhnqg5F>=`-yOs-jUWb!i8n2=8QRoG1bm|CyjE~W{Z;NQ@`+g&RIZYq?bJ0#{VSY?? z?s!VURn0;guMwZPNM_9GyW^b?gcosY#Z0};9ZgfmH40{=J}9T=yt5yvbNVvhVWwK$ zbW|=8n3N?pT4q)I26u>XInG~Kl!+RVr1gEbzw;vcA8r2waJ0`^9wWvve0aj{HJT1u zE7DQYJT3<-3#~?ML3#bo=y@^6Bj>9;7R0=Z7>_f2o=KS9V0eu6SXs_f#?_+yTAL1i z;5hD*P`F(eO#(4>IDZXO(mYZ&)iS0Rl;URPj5F-!C+mr6u9Hko%+2Bdawc&T5zh>L zFYVlUy|9y4crukg;vU2;(iFmYHgNk$Ow(dP-Lyln?XNyV){Pey3-s(7c2-!e63y+x zv>x@k7y-AeGBrs*BP7t!G;CEqo6D*Pc`Bo<5^^6i07E zvxN+4iv4;aEb}u-Vb@Oo#HFfl{+ZoO^_DMn7yC~Sq}kqfQ@&|1ehK;98jWuxRtpEa zlyGC<#==d2n*uij?i#q8;IiQ6!QBdXC)_=7_rv`W?s2#c20C|A-q0^~vQ-7vCS~=+ z8_oU9Z#k_ZFUX@%39SMUfi%r;r5RfxvuIv+FW04#;YVxfr)()j$ zt)c^5{tzTN=E>)9f=}~ zmw{Flon={NE^VJy%qfa|b~R%wU#7EBoIeAnvxL$Wm#g6MwpC7i{!UwbPW-&xMaH}1 z^AABUAcnZyA8m1Yhq@Gzv3YU1hq{7Td~*(U1xM2Nunu*FGO;;d;;I&h{O9O zW)c3K(=qSD_PipAnD0h-4O|6W4cq~^LvTmoDp418;2okMtWj!(=drqEoOn^LZHH~g za*f!B^F(@MQQd3@uJ5H*`vF}-UOUdWrp>cAnrsm^jhNu`_19h)BfjY~%9f;)gtYsJ z`1_$WygG$jnc45bN{)U*V5Rf9FHCewx(^Zr#yEcN`1!jZ@~4G~PorJbqG3>m#w|{i zVUEl@p!=psBlZs~j+X-ig@_nT5a^wj{nm5N9ogsHQTmXaF!6m!ojcc1851Tx?sK{+ ztvlf~7_XEc&l~Z+4|uo-e5qQ3)eK&&qJ5vjksR1G;ak#}gY{n0g>3vzlYb{);NjQH zzY{NH!5=68#$3pRf2{l)c_9P-@CzAv8TpxWGjn5YnK@bcYMbI*eF=J_ICapPmyxUE zGvmJB=lgsv444K)Kfx+M*vc?*O?U25ac#Zh|Q1gf`| zSjLF8EeeCen?vZ$w$&0-=aKC+Y5$;aLy^Y1`d`ilmkI^}++Q&K%UNZqDnBSMPT00` zc0=6#ok5CY%DTgjN71YQ>Kk@Pr~FpFYyZc5Ey!2*Kjw=am2bfn<^6qq4qc9RzB zXnzhrpl)P$XhhG@u$#$WTukGLhM=yWd^kqj^`V5l_Hld!4{J@Vc^(*i(h3V4eoKor zo`uCrnD{+Tl_bdCumg0Rv7w%OH1sdTl;eU$Ul*g_WX*A2n?F&|y#2K_y>&sXPMD2z z9VXMpbI;+;PI|WyGkS98(Jm&LgOnV%`|XPZIPtSsvm23ObgCig!y^n@$MMX1tH?5HsLgy_ie#@$H zl~dbwU0QP+ECUz=<@8bcoCp@B26{7ZB-Ff^g>3mDaVw`dCTjSIAy|3*R6y7JAEF3<%4pV=gX}ewD;7eW57c90_fW> zv_=#M&8J0~W%ZG{M^|jYIn|Itzrz_iv?ORAa=9eoyo=gm z(3lZgJZPT|;G}i&^Hw38cyNz=AIp~Q=Pa#JEY@G6Q)pY22}w=X0FSqTo4;>aD1y!W@X!U2&IeY3W3oru>q3i8|A3(^fuO?jBbp$6yG%LX}nk_=fc|dG@68p%}eCXbVc;%C+ezW0s6Uo$AJ_6QO?CWebw;ity{*H)+ zgWQe^SOU*n)N?De_tT<^>LFN)>C4Dm`kj~Y>mS+|bqmWMHDK64jUe5`%e^BdiT?6eg zoP0x;_LKp<8FHbCj@z<&e=nT}?y@}dJl!j$vj^Ru4LiUIc=-{p3N00sZ|=4(z`Lc) zHkFHr{~n;dS8JitiH?L3KWXtU^|!$|VD4?O^YbQ>$zcV-$0-$6lnUHwSK^-B0c)sJ z-t7m47QS}H`_?ngs_cyTgN`1Y$oThNwEUpk1$=akNT#B9K(?`e7$fFlFKX%<9lzfS z{R1zE-eaSBI`^0YcI&eI5yprc0y=QZ*`?1O6zDH?WwNgp@6=jlpI!cq%8ZFE$)@T4 zFb%Q1MW}C}an+Y{yJX*HD_##yt!XI~h>oVW2{D;+UgVPZSktqF3fL*U*hFK0lJhJR zOqmHh4dZ1qFFO>G`V-!#UHYto_K#BP3+Kx(mGkix<)lOYQi#w@fBe$eTHN1^%C0oA%2!-hGLZA7x*4pvFZ3N`4eV$;?zL`dOW^&rzW;rLH3;q&tsH_hAbkmGj9zlO}n*sa!3SoQkl63yjLq48(iFNYyR`K05X(Y&h@d2=$lBB~)NHaWn4RX3C+ombmd1vM(;q&&z1O>;nqz zkz=XvP1#p%ozS*I_W8rOs~`JuN+@27-fn^+I6o?53F}CwX8p9UF?r;iuW>Q z{=QfZsxo9u{%~C^E5z-mkS|#Mw1;GlJA(0ToaAJMpXAnr3^Ae|C;MA5jkaQ3G+J@h z0ByxudhReM}8oO}25>C*{zOXczFQU=t)H-Sv37J{A&?*+vqtca~${W9KG} zRh+IZ=5OWeupUWV8{%a`Zr?v8@vLjbTBVl_nUdJ!BvWVc*YT6CPHBJHt7ElEAvk4c zMSXDav|#_=q~N6B;K}4=ubJy~1e=z%uT!l$rt&?cc)QB})R4vdYv`B#JF*{& zTQ*VAaMTgw;ljPHTX{0Q9QV4ghPqLwUo3MWTJ$N-xz@U8qNJbgl!{x*g~S(}eoEeN z*CkVG4?9ASD?Am7Y*81)c``W;xi-0{A*IE^)KR*oiLF5#EaI9SM(hgh|CtT_YfmfY z5{u}t-vg|X<$u8M!ZPJlVoc_D@XBiJjCo^%t-Pu_hu-=(AI6&TuoPuJ+{L7-#Tzfa zD8=Z4aAFtCvD@%QvN*H!b-b_>U$!#H;{9D&q^JqEh`r@HqvFq8wGuB|Sj2?VVw||l zDxE!n?fl4U?|rXai8q5#(o72nH1Ma?#3B!!U%r@iQSwDu? z%DGylktA8f38i5TtK*|GU$9!vno3W^junH$PdJDeTgug$8pa3sO#4v+=-FOI^Ajr0VrC$JcL<$;ZCqHw{@kB%*EY-BnJ&kq=7!Ek-dl zd3|$;_@g}-@bm^ru$STfVm*Nzt-WuQnf0)ft-Co0bF}Gc@W$e0%9yF(p_-{?-IT|7 zmr)+?DkU@L^v!*Tx%s@~jvur8><>4ddQ>&%++X))iz*?LoewKFI$Y1i1G*Vc17JjwIK$tS6jpf%9uLZW>ibamESt~{m@QN zF!`G!LA0xB{uuNGBDzWwaDVBq);fn>{ClZdjK(-3;>7YDd@3I!YFTz05kD=}ym>=r zq%?lI8EL!9Vnju_riDpmahEIxuiN1bh|=9wyO>gTBKCxXmAU(+%qw%C=h7U(&KQvm zAK_qh%VPanX!L)z2DdRc(^v|7-a&c$SKKapV&ZuRbM5?0Km0g}nLtzKL$a(s%@5QZ;VgsEs@Lv2yR4(|2+oZFR1e zNhL{h`hMESq#i#-%a|FUbewx`0}YRix#bh1y0!QAQX&SIn)H|96?MTT%h{*Pc6&oeKC&6DxIGp>?95DzjK`2-b zMIdxHLLqV}1fd5I3YA0Lcbe2)-nFp8G@CzlT;@k&V2t{ued$Sm3$H9Sp@qk|3-IF4 z!*7H8=4(vK_VsTEJ^|hV*1(j@^RqRCF4Zk)IRzq)Q_}xaa{GBzP5eb z^vDkG#<8MWb;4m0FW7&xU!di??3m?_@3gk`MZeA5I0AJ-?#lthRAChJDt$c`K(%@6lRi}2y<8U_5Zd>0sdtZ$B zB*x^NzKng?iArU+(ec;mSl>4XDGY-b$n10^>Pv6T{3$k?|F`41Q)?j?o_%Wo()LHY z*6_n97c@LH4ItnWTWCVzF3 zc|Hm@-7&#I$$Iqo2*8H({ipHL=%0z05y-RYBzrC8E!8|{>ZxQ|jJ1_!FWm)M%x7f& zey~i6l49a13>~TUS)yygsTorh(-YgtWEz{y`{!M@?ymWqsK(pLcjkw+tk6&7AJCss zX^v4C-SvxM-9__5Mol7#eVDoRCuoX4uRr#@YupLNjaLVqaI_umJY|~tr8_|!hyO)^ z%3*ff9Ve-jrW_#&lpjoWZl}xz!>xQSxKJkfxiIBqj;K}Vhzj+scvmOnTI_vK&)(HcA_>_$3f z=#0C*FZ-=z{-OA1t#p)3`{sPydB@6=(frMP%t?zi2lEB}KV?$fet(18KN9Dhx9nTr zckMfD9o|U|k8UyRll5_@bW`s_-6GVfJgwVnr_9FIlVob<$z1RI}!4tpltc$Jt2Gm`(kERd24Xdgb&3f}SXBFQrj(V&5g3Fw$ z=H;D+U`Aq0AxPzw>Z*I|)LqU!cRTki z0<_3=)s<0=oQ`R<1}8!?D5L%nl=LV{s?#|dCLvp*S_3aIu0ERT23u5SeRz0v!^^thhV)=gWE+lo^u%RpKdLU`9rcN> z&()cbSMhsvz?Gg~3h5Wu`1|Ww7hu2CXz1NVsWut)`u>E^dlEisyf@@ZZ5Pbw0L&H7 zNv~`X*+k&-NPdM|B;zCZvyfSh%Wzl7EU zPZduEbf2X&Ht=HzcM|@+;I_3>PO5F{Q|inr|cdW8uyeN$z7N7I5NqiBfJiyB2QT0eh{ZbBcdkTB~IzYboi?pHurNpH|S|Ld5V?aFki?kx~ zRzXmJSON$R5WT`~&n*IPgISR7UayIhdz1D!wNM_YD{9H8e;_oYeo9Y=Fb*r_EAAy0hi6GF+sLvu z-y7~ui@cGQ&le!g)@z6TDGy|i@rHI0h`DRK-R4_MPtUL;cHnupn~k@jq}%QHcoK0B z#I8ZCLyEQeD%|6{;%9r*pDNQa@h_$98@2IOYunc ziQkUYsf~8n2ciV)1UCLFZzoo0Gj~zS1o>81iaQ`<9Vp|HmYiJyEN!=cBT$MvU@3Xd zuE5(jQG)<}wt60@m&=y4%-t2#RbHw!!A|Ic-Q885?T{?V*hM9xw@bR-E@?^I#g1@$ zmq=|X`9Fy7?PW*w#I@H88y^|4ByQ8bsq1@4jB|e$_h1cdiR(St&>^?6K3`}>W8Hwo zf@%;GH%;Igk`)|g1(fVr&|?sEA{Wab?LXG0L+nQ)V|bUJbYM+or9D=#w}EziA?)(x zfwdosO!10jSxtJ>7&k!&wFTeQ##z#X8#~8x^>8OqHHqP3wp?@1(4zkrwR1mZ)c?5P zPhq_6mXY>h&R7B7_D%mIZ9l;J$ISUX;Onkzp1g6Y$~6D|OCd}tPAtmi z|1e)~c^~p0{UGyPU}&mfV|#j_aih!d*rs7VVQTiwK-C5X{xm~v5;+cWp;nv01a+8q?lcqKS65RrC`>Og-*HA z0tpQz@CqLbH*}CRkH_v>-uSW%`l2P=j!yrLn~)^a;oItHoR=0bhAc_4-^=Z!`yOZ# zgdCUOS@y&t=K*KCQ+9l0)}pMvwV0Rgn#qp>*TVo?wakN^{*xWsz1=uj$Nw;ourYH! z(>PUWn(vnKdH6!$OqtGiSSOsKjsF&daqNSH_jCPwkhkN_q~h9!WDKi&i$T zQKLMh?1?;QrL!5ew`S#KRY|ocO$li6G3>Yp;_8;krTL)E|7S^?f%uOG^LnHMt+^7` z$TU78>-2vQdH6@H23Y4Qg|4Cv)?!HC@OkSU#7TX%RLq6c`{Tv3l2S1bPmdYoB^mV^ zbE%l)Dis%2%UWDBOT~Ga;f~{C&uXqgqZ@@8O<{rdbQ#y;no%mwWP(~;Pi53=?^Lgm zZ3=F2%|9*D^9oj5R@etJi{{LGzVYB^u82$H>V7s4**PBFcFe7!PA{-$j0 z*d*QSrQ!nDPVxTof`f;^zId<~))aM5lsg1N82nx3=O;9ol-C z^o;Xf^piuEMnd9a|6#`RBhDEEbZzUR>ryd5*T#928&UO_k;qd|Rsq6J7-A_$c#yFC z1A6=Br0PmTG}2{|r{O$KuR8sIzIhHdbET44zYFAy@$7l)DKBF=A^e0fnAS{zJ|UOv z5m-^b!g6+Zz-!Qt6`@JC>#Izw?ZGw0tz#K`lv>Db8Mg3?2wT%w_`nIR4UO*eJ)Ill ztzI5u`kwGM)=Dv*x1Q`#&_R%8R&IrMQYcC|mbq}2M!O@G4D7ntxbFTwy^XY8=v%xU z{yEt-F~Jrg*Xh5%?Zhw2`T?XT^oOi_`ENs36(kY&;#l8ceMVTBb^<~=9btQv^>$v4gZy!=X2|32kbfY zJP`s~Sp-^PELVj9we$hCECRJSGcS04riwqKG$EjyMP02&wi*_JZWzmXiB>RMLwXtV zalg`YXMN9hcGJ%J+yLeLFgrjuA)uQ@65U+wqMJTwl|`T%r{RJ`H*%_3+OF_u#Sx&fW^;`^iC54FOoyD1)e;^cK~Y^K@C`l2wtx(+#Em+)u$Iwk4U zyYO8EB>ZierDCFWk62vos8^W#Rx+01XO1=>yK{5p;YM8uTCZtO!O>=|q6cR9sgbZMK`IRDrjMJ%4a8JX|b%=bHP8i^;Nf#Whb_VY_0cB`Kr+!ZV*V%p4Tl zI6|gq`u@RzVtA(PWH2OV;BN!togH~Qtp6$BXa9?QTcmt<|5m;=Kj)i-e3SoUzWjfk z@7mv)FOPi3|Hphg+`k{sf%^XOcjlXdd?)&dYo+_^X05D89q1|(^tJG+N>=}8+1JhXJEv3yH)<8Zjll}g z*0Wo$xo5tgtb~@)sQX`PmtE`++9FHvPnsb?sFXE*KY5_o+bXM3+@h^?@FL6nWPQ^(86?AgcR0gm`P z^2TgD+N8i2v?OiXHeN% zX3%FByXQQtlN>K%lz}y-T{ry8J7&&TO7?i#9@oSEMJZ&$*pPdLeT`hm9YU$N)?I|S zC!f^abBN?t3bza$D7-T?S$Jz`8ph;K6W&^(8;)c2*y;{*<)`C6;TYj4^zoW;tWzAv?s?jemiZT>^;94t${4c}55 zpyjjHif;`Epz*o~F>U@2+K~@>eu;q~zVRkDy#p!E?o`fr6R*E@TdF64g+Mu}j0ClT zv?gW4c_l`ANYGc`^nVU(30WAw))~x6CE^%;iFh9Gs!ZtS+}ED>N%MHcp*-?I{dfVj7mHcK1}$@$ znbtAjSjsY5cFiC!3K{j`39@S`&=Q55r!NK2iB=BKcKy%Ltn8PNa{!qDNQYGh$fVzZ zoD0bQfK0Z^0m=UcxLc24sd+1IWzZfXoNv8-RSkss-ev-+){U$Q6K`Yz+eBl;41S z-ug89#)DQJ;2!!7xIbB)fct~B2jHgu2AtcP4!DP{!GN3g8*odlBLO$vN-GZ76a7tE zDzcgY`>0h9*vEb&&6ljffP37Em6pCQN3h3t z2CSA681qg4>>Ho(^DnmbOXEoom|1h6U#Gvh9dOq6MXCJ$MS#O9{HA|0;HF*tb^rJu zZMF0rcn(;3op&Dt^S)d8&-GEuQmTNzn=Y<`StX#=GVes&98mHh57CIHBgEb|2x0l zS_}N|{Q7%Fk^h_K*C5})j$hBOAGZE_es$11{4ewCR>1x5{Mx;eUVst)f9Kc#o95SU z-czf8opkn(`4&1%W37M2URGi00a@X2UmB-mCUK8&Ia~;6Oh>HiRahw9&_|ImMkU;7{8PjtmE zw{OO2ND(~yE@{cd3eaIC`PuUY9-POtvLHuBeXpo}S3jMDlaUxDtZ=8-YojzRuAFjwC0@Na2dhyADU1s2 zzL;plkGgnuSt_GJ>>PEAD?5a8fNVG3#9q9rnxkrQ&BiLyfY?aHMPf}?*P9E|C|g`l zb|A&x$OSolffaEzZA5RMhDA=ZI3MrG9Z4=3zW8jgA< z9JrkZEX+~hop|-aG^8q}b^)m&e%eRC{X18eP3w(skjpg6fl?7j;qHcczC@u1j6UL!n&^T(XMcCSNO-Su)Zt&aaTC3EBr%OxMx@R zNLRSG6b{rp!mv{`Sh0^~=-iEUU^Sjet3<+g1VZhqN z%ZK+&F=3f8{{)No2A;6|(bZ4jDM0_lM^`UMUITPZ-Z4AHfz^SyPbDt`fw;j^T;Msn zS|P#3R^RZ~+&FaMLCC-z-bu17W3Bryyw#|Y-D??ly>h1vF?RjYWxFoW$RszpMTayt zjfG|5an_&5tdAqugr#_QrV=&;rFZP867=2DoeC+Jdm~ z4q?2jKT}VQyZ|gA1*AR^{o%qiEhre+)esFbknK@~fih#!Do>z4GoUW1KR_xi3aL#) zdk&{jfv_LUq4ydz8uV8Ka6gLrdSbmQlvdK_tkicp%z9=ps%Lav!M!!S{~9<>+ZYy$+UYNR4eL9^hUh;Nh(sC_BOsm+fVC- zxB$&X>d{YOkCDc}hxLGsWhH9jwj-5P`c`M`{+j6LZO|NXKFG3l7Zqtgy86RnrpaqX zpJYHm2!Gra)^>$|>`9=wShHxiztAc{~RW^C~+$EP(Gj*1BzIp3n=j0A$f$VQH;l-+Dka z`|c>^)Ms(!;o6MLgDV*q=4jX@vXeZe6rMRPi ze~Z6{_{1+UMPg!RtAB$ulgq$Meu-fd00$29IBOO5nd883HYXM0{D`1i{e==F_)7*h zfuPS+?ryM$5cIaQaw&(my$almaB4T!`i58GTOwmE)t>I$Y{)GOyK4HDAYSn@u%;(l zgA|bZ6p=N{HqkrCP7s&d`U1+3sAq=k5tB0wF?&P@jF zb@Nxuqyy6rJkcDY`ndTk&elZKPfZ7mRgesoM^~VcUtA!w>B2YcDEHa6Rl3y5H&1m zf5`j$%OQ{HE(ew2BX|h%?IjtEmqAB&$e^8o$UF&p=y{7C zuE5t%H?Bng;m&`>)mh+*Xr;D`R)(zO)SC62`gPp@hU*5dJY4@uD`#<1P-F_f(>Pr4`Y?6nu|y)_dB zpVETzHd^TayJaA#8h>;u z+nJ_!+FACUcBoJ2puJ>Z46o?aRq+4C2(W&ssxD3DYIKdA!`D;}UUj&FHLkX?_g2{M zOd6i_WX_t(ct>fnsW2-`UGs&CJ7rq2(xC=zMDR1S%!M*g$8gAjUGmOY#K!g$CaC+k zepqB)H6v$wmhAN{KavL2rk5t&?*SDrEVF z(IFO3O<{hm>g&_Oj6fbeQnvOZh$~(yO{kBP>s_l|l;Tp6t%l`I{;HQ!)$`wYxs+4h zmtm)*8rKF~`aP(f{HMxualtj3bdsSnxLCs$SC*?tlvU4k7Kys*KrY=iVybXfWwjlQ z0xqKR<-7Z)L5fnRk1G`4f}PPV~6;?w+_-Y_*=*y;dx< z9^K8v$oYHD?iDW@LX*Zf4V$#0a<%;(@uEJpc6^g{5}pjCC0Xy5!Oxp$7+lN74iw0r zww6X}RTV;OuLhdi3VN^(dZ);c7(q46Z_4@iBj`p|5}N+F&WCAZ4w1 z$*J}2X_7TltvSBxn_k*`D9#fAogDt8|p^Q%LtMq`SO5Cw)%ZQ>iDsPo?#JiuRK4xaXv< zfprANSLTk&PY3Mm6s_;+_RhErUnTcMTF)vO`s;*86y@+JZ9aVo1# z>wB#I(bUH#=(8uMQ8{Cr#fw%iXG|ONrViNX)*0jSws|uFJprjsXn)t7kz#SR`EBr| zyk`<&@f{K4fn|v>KJCmL#*!;cLp`?Wzp$N9<(%%D-{5*L9~uwvdvJfutlFODe@xkt zf6oL<64g}($#kmerH<~JqK?)+s(U<~2^dA&l~Kwq!*)?=6S_*PYQHnh-t=zhTJbV7 zEkDK`Y@Cz#o@-NnpZw|B+wv~EbjEtrvwuol-VUee|EXhpKJ+Xm#3n^)DNj!1(zoY# zW0`F1wE4GvBEak$=Rc^_^(zm6F!fX%06=J59VM<^8-JZWB204X#qJ zGDYUv@PKJ1lRMp-c9T^9oHG6ccT4cfw+dRa32HvSMMGV z=6ri01>f>w`78|+9Z{9!W93u!Pae>VPlJbvp!{I|T?^+~l@Bd`Shs`RD8|gy{jIzS z+Eb!`VaI|#VImg=J>M~Oj$Ua0hbKFOWNTH>Gt~OVx2*?_|3$oLlUdf|1WHk31l0&` zxoH{j>&A1Ldw>-8sXNi4_w*e4RqcA5%dUs)-x<99s9?SLmL9%yeX9yOasIv>VbR9K z9q=$lcAy&T)u@|$yvHrg9{NgV`6oPoK@WOq%7m?uu=r4Xh*{tIpy4niNZt}z=3B9a zfv!=1t;@EXiLGwBUSQ9oxR3MgK92j?6?m4#Dv{cQhLICbI6iK!4vTdm9rXK;-MT8q zbYLDqEp_7G5T7zBzgRS7#yZ@hNg5d*EnrNC=6OV!lQrvM(Y7D#RHzzka@N}4u}6HQ znr*Plt1I8eti=w5Envm~X@b}74JULJNAOMmv^>6lN}%(`2riD(V_>eDOgv2#rqi01YaPb&n5F8-Jr!7#x z(%*M89`B+PviG|ZbU?JR3Rf?Ld*YJAPE!x0so8}R=#f&UaX-?qLgT=fRPauto@30w z58nL2d7|Ft0B5-mXSfry@GPQ&1^$C z*11`PUqpBt(!?7#hwuvszb}RLOJxW@gK&9QSdQ>Kg!f2c{2zt6A?Zph1&}>2qz=_nG|j<&?0;v!e2?@e-;EGd>6uk6mBfg zAv_e}Q&QMl&;#K?2-isAvjxEj_e1!5DSWaZ1mRu?*CDK~3Nc3s)G|{l){B>69eh0` z35fcG(1w?-4_*Y8t<+MH_#)6!_Z7&@fUv%Nku^srydw6{e<=QBP{B^7?6s+j9&v_8 zhPD+p{zd#rf5O{f8Q1d62Yd1Tu!89ruW~;)H1TlS_Uv!53(v`>d{sxcuZVQdLOyR< ziJS1)2MV;GL_yYvFYZa*leQ-NyBSk1R;HDRgTNtN(sJ;F%@-N-hhi4~vAM0BasK=aDX)xs_z7U`BnFaIF}~*=BH8%?H(5+ zl(^SJK7+n_GPNX+dg!bahtD9RKH27<-M0NgxKGYJFFcBw(ozwbuR)7mLfOl#1WWkO zZC)xtufKrSwGJCf?F>6B8_}*-Xo}&IRH`v+WO46I>*3b;G4Qd^zh z=|9zW;^I5d7Z0&at#zaV&zptbPd} zRO7#^mSLrmcuIbY9v-+r<247GI*fLmY~XH{n!gZ}wiy;t$F+>z*2|v zri#Wdn)7I$qcQDIxM+@ZM=BYWdD=y_c`B&-j8|)(a#3TRbTQbRd9jE2{);+u`o$n~ z>cx;$c&MJ4R}qYQPOk``IX(Z3D)h82l{KtEd1a2J_;-yby7hSo*L^EWa{A;Ci@6NT zGsGjIJSlIQgNv_(X(_JwO1N|{7pF*ih~=1(0`W7t-p}m1Khbrc)peIkcRhG4`6jZo z0(oKR6|&pf{fVvS7aA&5;Aoa!Q7%4pMFmbK`DDV%F6(^Ova3T@nx1*->R%QYUEx+1 z!PgpnGqJV&YKj(ikzfs0f2b61n!SA520Q6ez}lFQgy-i4vc%+j)t<%-F&MeQQQ7xF z&K>J{joSC(&9Q0Roht0Pc4KzJT{RkOJM2k@WYqII#JSbxdnOzg<;9_O+?`qhmTt@T zi1PC9!0DTo^acKzVPib(=vg>d>6Dqf-ZOa5Nbea#;0q2haw$e`V4`b<8Ouoylob{v zvnST^Y%%$JU4hxx5UY_J^jfxTFr+D~dfGudxJFGa*LbyqzwrhQKIQE(_=Goju;2|D{55bw_Wwl4hL3@r%1-FL zE-kkR<*jmSFns8)vd0KP{a)@5k1%n|t2j$(6+4g_I6FoyttWf#4_L!xK*0^LnA_$* z2d{kWwh!DpvKq21Sv#B!PR49`rp@2lWnZ3Fnq)th?YgV?Qcj$Odcqxo%wiHcM!$(Z zhHK^KN||M3RryuhN?CSDDp_}ff37QXt>LO#OZ2WjB+J$y{>>}Ol_mB@*hKyL&6`)` zD`{VtkT7}FY}@2f54^*v6B162((k35C{L2p=PY+4&lD-oLE3Xy26g4>L!Rbr6D(eq z*yB9;u$4(Yv<-e%%`Yt6{onM^HvjXT(O08y^-yzm52SkTiWVh4A=#Fr`rQseefUT3 zIh6mH%L! zJ_&ul?3uGOTBgn{5~r3OgRE~xi+%FoCt(lJsx~WqY}?PdIy{-Y)u{H})w&J2*aRJN zj2HLed6d8y%_&M>O{+0Q?la$zUn~0K06Y!6(GUMaZczRsk$+1&_?m|8*+rrnJ~Nrg z$3z{Q;hUa$OpHL-2|dSj_Fg_T<(N1yUX4&DdrZ`4>0m|HH(u#G2wzeL*z&+QWU}IZ z(5p^q^KZF9{g1{Y8i#0n+KaZPG0HY@BBMv2d=YjAX(oE}3M8XS17Jn93$NYlr#_?Y7oTCc#kc}QH?vr(K*+B^wf2CivvR` z-UhpzzP&xunZd>4Q0UySI{7vEACK7HZj=WzgBI}ED~{H+$(n=6KK!69@Cbj`eqi$H zkL*`G$~MO5JJkOcgu1G~<6YPey6S~}M$~&iOfPr}8w$@$p(j6&3`za6Qr{2h^<9O1 z)>8Ne2uke^>x5#2hog?L+dS{&W$eeY%iEiM?n>8qdD=Ckmx%NxBmjT7r{F#yrcYPe zy^wZjnttyWl`@n$9F`VlB|9uSCU4pgPT1ww~r%h zHaxPYH+z4bhg!f^Cip@o5G)qt>Y!5F@LJh_{NNT`@YW$LBI^w0=*I8U`L1 zpEk0wirSOn^YMCKn;q^5at63++U!SiO%AQ|3hG34A^gS7K4e2bB+6Xz&^Wu22 z{=ihxIpSi()3bh$=Q3k=WWUYJv)@jpG#50;WU#&jNURUw`>CkBSX{)ylBt35bt>vA zzN!4W;`7P@F}-U_#i8(C6n1h&IO~*x@yqA0UslcsHJsb5}uk9>~di}-Um4d zsx-OYv;S9Z+-)zMF?+-g<_P>CM^t!3QLps1K|e8ID_jqo;$#_~LaRkh{A%<-Hlf6> zIfL=yfx2v^@24(U8c99ySIk~}$>kB5P)h|)a9EX3-3A{kteNLhRlb*7X%36u#i?Px zIGy~sP>(a`o)&G!a9Hn1sCX;86z6Ca?bjQBaGJY%+f@(T70pxlZggJv65R}l=~qMk zV1^oWypmsS?{#uzWUq;g`9viw-38TF;%wU9Ly%kip3oG=49gDFDQ>&Hw-iTwOx?t2 zq<5`zoj6p_nrB7I;4>v57rszr3A3M~yL|3!*CE-&hF{^#0MEKZtApSX+F&Y8W=^`s zZ)wnROykq6tRbB_Q8@`BBu3bFRqt){SG3B~&SXbKlK**|Mtz9vQPVm$m>-(1;+K`j zO|Vk^7o8LLl;Lu>`J-_}Wp;pNGwwvd% zribx$(bH3NWWLlJQ^b8^dWw7E6#Vp@k-TCJd)GGXL3)a>MQzr}OJu$a9e{{BE~?8X zJy!+T6%*S7ip(UzWk#G{f{)r!#>K6Mb~`BjaA^0a2u9}W7ntIrl; z{Jw;%7#FyxRys4_V)T35;+RlficXNnXoa9unNdA3C@s{fG|6FuJka}WqWfoW|a~8Lzw%Y!z^*d_8j8l^{}H7)hU|ZnJUc4XrRoA?l`_IDnGWuhpA*UDrlK3UG zlU|Nj`i8c7e?`9nG~{7J!T~HaD{!7Qw{G{f&z8U<&5DV&@#)d?-golZi8%?`!!jz` zo155C%DP$V_nj0I2m{;Wpo@{fo>*mPg^%q2toZ=;kq$#c%T|jt8~3y~Hd=T^qd|!s zmrox|v+8B%v>YzBk6?)9JnWF8ut1|u*llm|lqIi4Uyl&l_bXuM4gT|PfQvNma`uae zg+&L4!Y4m0!B=1O2BmA$4mZj|)KyXZ4)2QOpFAw|nVDhr&~ZH{C}8hAdfpi?W2^?9 zGGU7uYNzG)ebU@O?ZsH;dC>P9cc&MY7r_5?rxHCcfA#=&(2N1*0Y&2M%oBZ2=s$+% z8?GTS^hm>zc-+HIR4UrIrek8W0duS6fvC>d&ntt4jpCh5r(ft?fji=(PKi$$6w;h} z3{S7YiVI?_PaQla?tu4zqNkIRzw&6{MSvZs^xfCCk!C&Ed`#ix7^h+9GHYTs_%(U# z=;5Y^ZQI6&q^%GaC-%(_PTepEBj_4gHn)BHoG|zn*)V>ExCoZgN$c$g2=y)-}4x`WnUjenJF6K7)O8N&w5n}dHk8m^N%DP zWDQ;V3$OMWK=Zezf#Eu%(Mm!|vaA*w1h&74^0HNSpJ%7HSK7#IO0jxQo4*tu$hyDO zr!`@*b>d#F*+1G}h2XI@5G4B+vYFvg`>P2_Z zJTTzHzB((_6f}bJHiSZaTNPQ z<*Tsy3py`M-U-{~(mab(M9i~=xslo7*=FAWS-8`k%Vuk{PpUdjvCrLSwr9&5%b8WS zaOWxTocmU=#Y*4Qo9qTNFHaqARQkThxgMoJb1_rZ1M@8LWm^3|b)Ld%>}cdni>g+h zc1(Pp?VBB5(RT%Weqxj(e+TFrkX;>Wjc=1(?=AF6cSHM{=BB+E9ft8rjKXuRa*M8N z7!OT%%-H?y3J=kCdh!+WO1BK}v0HiBm5Eln(Hgyfd!t5c;157&Q(ZHB*yUPNZjES* z4|jZq{~x`b{)0D~rMaBwmezdN{eutF-1tAa6pl58_+f>gd1w8k4c;gd@r@kGM17JF)g0%s$U&&4RUuA>0t!U1R4|jd`VM zi<4`3kTdg@?bn(js`P`ncI9<-m0=Lc++4)CXsR&8#=`1cwD5SAI$;&;lCndXLFDi3 zVjqQXMthO?k_#68Mt53NZ8D!qn3)TmR8xeV6KF-Ww4H?oJNeKnp1NGt*y{fZw5z#g zY538s(_Vsl04p@4W8LWOn>HZ(IsD6u6(xPsl*K&_s*>w|%MEyu*sXrAN26RP-`r^m zy)0wM6PaS;NJu!5K4Gi>^;Yj}yEl-_#x~}*Tv$GWXUsL@)3FPD0JN>*AM)1CK8SIc z#v~OJty{%(q@MIX4(R!U6w2N zZE5|>>}7bqBBj&m|9jO0W<{m8BGJdPYOT9IRG(sVR|Zv7JKUA9#K+j`Sv~E`urJ-) zGi4pqGo5W%s$;SbiTb)h*^2hyYfO7E>=vn=dP6vl@R-IfotalE_oE0KY; z#-1bw9_6?OE}U!lm`enYJW(9ZxDL@8h}J^X4+ij9L&x`mt&GS4w8}jzT8r7(vlwAm zSloF5?cM5c7F+$TH|%}0V3jsCd0~TE_Vh_+(5cJO=3)F=`ya74T6A)GYIAn4>|;`LvA0|uz%1K zmixAv=WWSFK6z(y!m`s7D7O-)%6FuFYp!u#x~XqA#eAMq<5a1_2cy>aaFU^c)h?bV>+#-q-?B4;+RDyns$&M%%|jdi&aOh&8U zvHu&fD70@q8}h+zrQHTuh_)H9)se?kvgiT6c6PJ7vh3R8JX_P(uJVRNNYEs5VQ0P( zQ$p8N($_PD%4>_4%s2u60Uq?DZXz#7h;3_sJcqa$1l<* zt*K--GBFNe60@fA8|-p}>f^r_4e=wr zf%I=rTktZQ&?{s22<2Xxu;tp~1#2pIpwGT9`O1k$-<&c<#@t4Ojsd(Nj)(`>9sW}o z%ag(=^eskBvjRqY2DZ36H$bk5p5&eT4pI+(E4k4_W1?I*jW5~JpWr{RUTO8e-Ej)s ztW)CKhU!Sj-dVF6WGw6{S%)W(Wz9nS0(g`8_QYCoWN0TWAe(c)w4G7?&uOG%4B|1X z#(s{Kiq#kmkB!NqoBx}))&HE7;&A?#>4i5A4@NFlMKM6TocDPS z3yCX-a~*8V2G$0&Qc(IPV4m#mkryxzpe*q;a?<)>k>h!AE8|H z(XQ4*jDh9xU*!W1SCseW`| zQViOQ8%{pPf_RQsz9)x25hXOcZ{8fmYmH2Hr{CM23+!xwhlmKCtaxF}9B-OfRIcRL z@EiEo#gQ%r=n?b^>Fv3C&AnUJ9^gbAds$4=|{g>cw0ue}+75kdN`g4*mW>d0YMMZF|IL3kaj9Eh!-fPgxxKU^HY^=3bbFFAm z1t=&myN=Etf?Wenaj;^#!_(@Y&^i$Oj6!=)PhB?oePyx~zV^0O|A%cOoHfvG@9iP?ya_U+`S=!u~Df)t?TPHQ2*GZuodO?%`)Djo1mDQB|F; z6*H}8aJP8*zCW~p;~qNXLTlI7gSm$KLu+gddw;3)WlPvzFI5}v(hT;WUG)_}ml@>AqNgZ!VKfN#u){5R#$3b4QpKY=AQqQU#+TeNp^knIgO z>|$S&Kevy^-qdhlKd9)LN0UX*c%Ip`fwx`nkZrOr6Q3?1&m`ge@PxmJ&&Jb;H_;>c zv3m8L1}(=9ly;K6Ie2mYwBggc>3+RZ)26(By;8QWKOg&OR)5+_-YIC>IA3n=NSsNr zo6UGWsv3Bj+pKP|GpwateA*R{F>fv-kG@ZkML#CUqcdx^XOBsXp0~9fQq(#X^u6Ui z_f3LX+MkULt6?@Dw6iA;R>G6c=KcjTxQ(Zx2Hb(-+slL|+20 zdc%0G_yz9J9v5YQBU+dece7$X*gg{II&MuGt2C8C(6J_ ziJn&#xip(=SNT6|@9>Y}<-RdeISzO^-;MTt`zeL8Mh(ZsR@9sp%UmCLw`ceGMaetW zC)9GEQTpmlFTvM)qvtKluE9Su(}hF;N>2h#6YLHby{VJlsHgm5ug6CyT|! zM?S)eM88Mn(Y3K1u#K4*RWAphfH8IPu4P+c+pSo<6P}i|wV)FN8xJkL_|@PJgu?66 zFn!;O0EfyY`HSlG*B)l7R%5*kDst$+r&D0|v-$SAF_JiU_t!|8eKKEm_A^oVJ@UvS zj42xH-othmsC}-}3NIDUw=>ZW0U@io0{e+fvbpk_TjH-3D#kvVx>g($Pn37Pa(af` z5`lBbXnvA7*Z`?vSd06xy~!^x;ntsq9FZ~ixTtgYfo$@Iyue$gBm@2(+0{n$@+Vtq z2lNW0HTM|Ixsmq6f(rY1D#di>*yY&#W0#{x{?)4)%iX!Jmb3_&&OMOKi-3eaxNE)` zl=6MpV0{n;bFQVVBMQe~W&ttnt&LZ;W!(KIVz7Bttv}8_i>t zYbddewnqpY_J=k5$?{M%e@87l)W|DpzrhaHlpF5&6nARjLU7uFPxv11^#61-uqQr) zeT-O0Rwo!s&nm2040T;=3L2SKlrts7lX_iD!LByF9NZ{X-`!tU_TUFt`q%UZS8k|5 z&X?FDPHv7Yo4|noOS}%UCeIGvU=I`IvDZDD5&v^+A*7bPZh7oA4>JVkdJ-S^oH%98 zM(_fOZwS9PmPS~{)tWTmGpe?=`WK2D!DnPF`QT>JS;N!cVJ^^b#0kSj`?CUZ9T(PU z8ZUt-BywD-Y;6?r144!|^wdA;C6OzC-wR$waewf2yUWmCA$@CNHn4Sp z)K6Bj!?_qLg{}r)q7?MsC#fKi63S7Clu!cw$;yrPpK93k1zyxualosEybQN-!}$4L z`D+TkyG`%8q1cQzeB?E7A%Lq);3_j4blZJ4*yB@9P2+Iph`>1{W29LZI)tG#^Zxm^ znRkBI%sUD*uL(2nQ@72$OFZ1_|NqUpqkl1X$$g)+{Wr62)Ipkc^Thvl)?IH;sQv9( zw?HZf&AN8+_s_adN?-lES(kV=x6iplFS{JE=@~6W3ot(nGJwZ<3LLc>oGlHWkaXRU zW6s0M>F$AP@-r zNAA*uQaWGC9?Q2DOGA2rb@%dlL}B-~kDh??Q1j=(d=_UNsZb5mJ_;njO; z5T4}OAKc3#W#upst)YLZ;Nlqjq@qRhqPx83FtXy4p1A7_bH)QYMh7~n= z^Pv0mr*=znJ`yJ)x>2|bJak4gBK1u6XZ#oZdFOXo@@p?GRA1GP%4jiU%Hb1U^fTbA zXyR2kJAH09?6=0Z?>}|fws#b~DM@xt_jdY?Vpm91mtp^c7*{JGA>DiiYZ6U7o6zmW ztq38H7jdB(JkL*zLQ2EgcFwf5U9q=U(kZX#{~WrF>i7cQYGN(R`OCs7oa`>e{_zw} zdLy7Q+P6ZnTH)*6uGnbiRjCxC@NH~c35arOjZVCCTBFy&A26N!s!^KUPU4@*`C%zV z<-|KPIIHKpE)L9uH%v$oFrND=I-&nNlu3eQgsiqR)+WetP8JsViYV8U9JkIe`k)rR zclK3j&8M(u=b+8B8T)t@_VMg{6eId~b?Tze3(U|TP+|(N!utWWf#(GU*YRo{y zx!wT�xhK`)M8y>T=~cKk;MV+nh~& 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

Xos6Z9l3cx<{C^p#BN&G@#nk&WU!x(RjXqoqvJ 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 Date: Mon, 3 Feb 2025 10:14:42 +1100 Subject: [PATCH 19/19] AP_HAL_ChibiOS: put tables for declination in external flash --- libraries/AP_HAL_ChibiOS/hwdef/common/common_mixf.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/AP_HAL_ChibiOS/hwdef/common/common_mixf.ld b/libraries/AP_HAL_ChibiOS/hwdef/common/common_mixf.ld index 68895095cc7041..79e175b2636ef2 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/common/common_mixf.ld +++ b/libraries/AP_HAL_ChibiOS/hwdef/common/common_mixf.ld @@ -74,6 +74,7 @@ SECTIONS *(.ap_romfs*) /* moving GCS_MAVLink to ext_flash */ lib/lib*.a:GCS*.*(.text* .rodata*) + lib/lib*.a:tables.*(.text* .rodata*) /* tables for AP_Declination */ *(.extflash) } > ext_flash