From 2cde55d4018f7b4adb296025420279df75a32b44 Mon Sep 17 00:00:00 2001 From: Iwwww Date: Wed, 24 Dec 2025 19:49:58 +0300 Subject: [PATCH] Initial commit --- .clangd | 8 + .gitignore | 58 ++++ .gitmodules | 12 + client/reveier.py | 41 +++ firmware/App/Inc/FreeRTOSConfig.h | 31 ++ firmware/App/Inc/tusb_config.h | 31 ++ firmware/App/Src/main.c | 98 ++++++ firmware/App/Src/startup_stm32f103xb.s | 364 +++++++++++++++++++++ firmware/Build/stm32-usb-freertos.bin | Bin 0 -> 20136 bytes firmware/Drivers/CMSIS/Core | 1 + firmware/Drivers/CMSIS/Device/ST/STM32F1xx | 1 + firmware/Makefile | 103 ++++++ firmware/Middlewares/FreeRTOS | 1 + firmware/Middlewares/TinyUSB | 1 + firmware/stm32f103c8.ld | 149 +++++++++ tests/test_device.py | 40 +++ 16 files changed, 939 insertions(+) create mode 100644 .clangd create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 client/reveier.py create mode 100644 firmware/App/Inc/FreeRTOSConfig.h create mode 100644 firmware/App/Inc/tusb_config.h create mode 100644 firmware/App/Src/main.c create mode 100644 firmware/App/Src/startup_stm32f103xb.s create mode 100755 firmware/Build/stm32-usb-freertos.bin create mode 160000 firmware/Drivers/CMSIS/Core create mode 160000 firmware/Drivers/CMSIS/Device/ST/STM32F1xx create mode 100644 firmware/Makefile create mode 160000 firmware/Middlewares/FreeRTOS create mode 160000 firmware/Middlewares/TinyUSB create mode 100644 firmware/stm32f103c8.ld create mode 100644 tests/test_device.py diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..e8db998 --- /dev/null +++ b/.clangd @@ -0,0 +1,8 @@ +CompileFlags: + Add: + [ + "-target", + "arm-none-eabi", + "-DSTM32F103xB", + "--sysroot=/usr/arm-none-eabi", + ] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b48d248 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Created by https://www.toptal.com/developers/gitignore/api/c +# Edit at https://www.toptal.com/developers/gitignore?templates=c + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# End of https://www.toptal.com/developers/gitignore/api/c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d21b514 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "firmware/Drivers/CMSIS/Device/ST/STM32F1xx"] + path = firmware/Drivers/CMSIS/Device/ST/STM32F1xx + url = https://github.com/STMicroelectronics/cmsis_device_f1.git +[submodule "firmware/Middlewares/FreeRTOS"] + path = firmware/Middlewares/FreeRTOS + url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git +[submodule "firmware/Middlewares/TinyUSB"] + path = firmware/Middlewares/TinyUSB + url = https://github.com/hathach/tinyusb.git +[submodule "firmware/Drivers/CMSIS/Core"] + path = firmware/Drivers/CMSIS/Core + url = https://github.com/STMicroelectronics/cmsis_core.git diff --git a/client/reveier.py b/client/reveier.py new file mode 100644 index 0000000..4e5af32 --- /dev/null +++ b/client/reveier.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +import sys +import time + +import serial + +# Настройки порта +SERIAL_PORT = "/dev/ttyACM0" +BAUD_RATE = 115200 + + +def read_serial_data(): + try: + # Открытие порта. Timeout позволяет прерывать блокирующее чтение. + ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1) + print(f"Connected to {SERIAL_PORT}") + + while True: + if ser.in_waiting > 0: + # Чтение строки, декодирование и удаление пробелов + line = ser.readline().decode("utf-8", errors="replace").strip() + if line: + print(f"[RX]: {line}") + else: + # Небольшая пауза, чтобы не грузить CPU ПК + time.sleep(0.01) + + except serial.SerialException as e: + print(f"Error opening serial port: {e}") + print( + "Hint: Check if /dev/ttyACM0 exists and you have permissions (group uucp)." + ) + except KeyboardInterrupt: + print("\nExiting...") + finally: + if "ser" in locals() and ser.is_open: + ser.close() + + +if __name__ == "__main__": + read_serial_data() diff --git a/firmware/App/Inc/FreeRTOSConfig.h b/firmware/App/Inc/FreeRTOSConfig.h new file mode 100644 index 0000000..5fbe218 --- /dev/null +++ b/firmware/App/Inc/FreeRTOSConfig.h @@ -0,0 +1,31 @@ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ((unsigned long)72000000) // 72MHz +#define configTICK_RATE_HZ ((TickType_t)1000) +#define configMAX_PRIORITIES (5) +#define configMINIMAL_STACK_SIZE ((unsigned short)128) +#define configTOTAL_HEAP_SIZE ((size_t)(10 * 1024)) // 10KB Heap +#define configMAX_TASK_NAME_LEN (16) +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 + +/* Cortex-M3 specific */ +#define configPRIO_BITS 4 +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS + * standard names. */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler + +#endif /* FREERTOS_CONFIG_H */ diff --git a/firmware/App/Inc/tusb_config.h b/firmware/App/Inc/tusb_config.h new file mode 100644 index 0000000..9e8ef48 --- /dev/null +++ b/firmware/App/Inc/tusb_config.h @@ -0,0 +1,31 @@ +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Выбор MCU и режима +#define CFG_TUSB_MCU OPT_MCU_STM32F1 +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +// Конфигурация OS (FreeRTOS) +#define CFG_TUSB_OS OPT_OS_FREERTOS + +// Выделение памяти (используем heap FreeRTOS) +#define CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) + +// Конфигурация CDC (Communication Device Class) +#define CFG_TUD_CDC 1 +#define CFG_TUD_CDC_RX_BUFSIZE 64 +#define CFG_TUD_CDC_TX_BUFSIZE 64 + +// Endpoint буферизация +#define CFG_TUD_ENDPOINT0_SIZE 64 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/firmware/App/Src/main.c b/firmware/App/Src/main.c new file mode 100644 index 0000000..fcb0322 --- /dev/null +++ b/firmware/App/Src/main.c @@ -0,0 +1,98 @@ +#include "FreeRTOS.h" +#include "stm32f1xx.h" +#include "task.h" +#include "tusb.h" + +// --- System Clock Config (72MHz from 8MHz HSE) --- +void SystemClock_Config(void) { + // Включаем HSE + RCC->CR |= RCC_CR_HSEON; + while (!(RCC->CR & RCC_CR_HSERDY)); + + // Настраиваем Flash latency (2 wait states) + FLASH->ACR |= FLASH_ACR_LATENCY_2; + + // PLL: HSE * 9 = 72 MHz + // PLLSRC = HSE (1), PLLMUL = 9 (0111) -> 0x001C0000 + // USB Prescaler = 1.5 (Div by 1.5 -> 48MHz) -> 0x00000000 (PLL/1.5 is + // default? No check bit) В F103 USBPRE бит в RCC_CFGR: 0 = div1.5, 1 = div1 + // Мы хотим 72MHz sysclk. Для USB нужно 48MHz. + // 72 / 1.5 = 48MHz. Значит USBPRE = 0 (reset state). + RCC->CFGR |= (RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9); + + // Включаем PLL + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR & RCC_CR_PLLRDY)); + + // Переключаем System Clock на PLL + RCC->CFGR |= RCC_CFGR_SW_PLL; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); + + SystemCoreClock = 72000000; +} + +// --- Задачи FreeRTOS --- + +// Задача для стека TinyUSB (обработка событий USB) +void usb_device_task(void *param) { + (void)param; + while (1) { + tud_task(); // "Сердце" TinyUSB. Должно вызываться часто. + } +} + +// Задача CDC (эхо) +void cdc_task(void *param) { + (void)param; + while (1) { + if (tud_cdc_connected()) { + if (tud_cdc_available()) { + uint8_t buf[64]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + tud_cdc_write(buf, count); // Эхо + tud_cdc_write_flush(); + } + } + vTaskDelay(pdMS_TO_TICKS(1)); + } +} + +int main(void) { + SystemClock_Config(); + + // Включаем тактирование USB и GPIOA (для USB пинов) + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; + RCC->APB1ENR |= RCC_APB1ENR_USBEN; + + // Инициализация TinyUSB + tusb_init(); + + // Создание задач + xTaskCreate( + usb_device_task, + "usbd", + 128, + NULL, + configMAX_PRIORITIES - 1, + NULL); + xTaskCreate(cdc_task, "cdc", 128, NULL, configMAX_PRIORITIES - 2, NULL); + + vTaskStartScheduler(); + while (1); +} + +// --- Обработчики прерываний --- +// USB High Priority or CAN1 TX (Не используется в FS, но полезно объявить) +void USB_HP_CAN1_TX_IRQHandler(void) { + tud_int_handler(0); +} + +// USB Low Priority or CAN1 RX0 (Основное прерывание для F103) +void USB_LP_CAN1_RX0_IRQHandler(void) { + tud_int_handler(0); +} + +// USB Wakeup +void USBWakeUp_IRQHandler(void) { + tud_int_handler(0); +} diff --git a/firmware/App/Src/startup_stm32f103xb.s b/firmware/App/Src/startup_stm32f103xb.s new file mode 100644 index 0000000..7614285 --- /dev/null +++ b/firmware/App/Src/startup_stm32f103xb.s @@ -0,0 +1,364 @@ +/** + *************** (C) COPYRIGHT 2017 STMicroelectronics ************************ + * @file startup_stm32f103xb.s + * @author MCD Application Team + * @brief STM32F103xB Devices vector table for Atollic toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M3 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017-2021 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + +.equ BootRAM, 0xF108F85F +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + +/* Call the clock system initialization function.*/ + bl SystemInit + +/* Copy the data segment initializers from flash to SRAM */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + +/* Zero fill the bss segment. */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + .word WWDG_IRQHandler + .word PVD_IRQHandler + .word TAMPER_IRQHandler + .word RTC_IRQHandler + .word FLASH_IRQHandler + .word RCC_IRQHandler + .word EXTI0_IRQHandler + .word EXTI1_IRQHandler + .word EXTI2_IRQHandler + .word EXTI3_IRQHandler + .word EXTI4_IRQHandler + .word DMA1_Channel1_IRQHandler + .word DMA1_Channel2_IRQHandler + .word DMA1_Channel3_IRQHandler + .word DMA1_Channel4_IRQHandler + .word DMA1_Channel5_IRQHandler + .word DMA1_Channel6_IRQHandler + .word DMA1_Channel7_IRQHandler + .word ADC1_2_IRQHandler + .word USB_HP_CAN1_TX_IRQHandler + .word USB_LP_CAN1_RX0_IRQHandler + .word CAN1_RX1_IRQHandler + .word CAN1_SCE_IRQHandler + .word EXTI9_5_IRQHandler + .word TIM1_BRK_IRQHandler + .word TIM1_UP_IRQHandler + .word TIM1_TRG_COM_IRQHandler + .word TIM1_CC_IRQHandler + .word TIM2_IRQHandler + .word TIM3_IRQHandler + .word TIM4_IRQHandler + .word I2C1_EV_IRQHandler + .word I2C1_ER_IRQHandler + .word I2C2_EV_IRQHandler + .word I2C2_ER_IRQHandler + .word SPI1_IRQHandler + .word SPI2_IRQHandler + .word USART1_IRQHandler + .word USART2_IRQHandler + .word USART3_IRQHandler + .word EXTI15_10_IRQHandler + .word RTC_Alarm_IRQHandler + .word USBWakeUp_IRQHandler + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word BootRAM /* @0x108. This is for boot in RAM mode for + STM32F10x Medium Density devices. */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMPER_IRQHandler + .thumb_set TAMPER_IRQHandler,Default_Handler + + .weak RTC_IRQHandler + .thumb_set RTC_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + + .weak USB_HP_CAN1_TX_IRQHandler + .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler + + .weak USB_LP_CAN1_RX0_IRQHandler + .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + + diff --git a/firmware/Build/stm32-usb-freertos.bin b/firmware/Build/stm32-usb-freertos.bin new file mode 100755 index 0000000000000000000000000000000000000000..89282b0ccd2f8426a3511047add4a6d9c2a7518a GIT binary patch literal 20136 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 + + /* The program code and other data into "FLASH" Rom type memory */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data into "FLASH" Rom type memory */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { + . = ALIGN(4); + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(4); + } >FLASH + + .ARM : { + . = ALIGN(4); + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + . = ALIGN(4); + } >FLASH + + .preinit_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + . = ALIGN(4); + } >FLASH + + .init_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + . = ALIGN(4); + } >FLASH + + .fini_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(4); + } >FLASH + + /* Used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections into "RAM" Ram type memory */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + + } >RAM AT> FLASH + + /* Uninitialized data section into "RAM" Ram type memory */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM + + /* Remove information from the compiler libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/tests/test_device.py b/tests/test_device.py new file mode 100644 index 0000000..e354ea9 --- /dev/null +++ b/tests/test_device.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +import threading +import time + +import serial + +PORT = "/dev/ttyACM0" +BAUDRATE = 115200 + + +def read_from_port(ser): + while True: + if ser.in_waiting > 0: + data = ser.read(ser.in_waiting) + print(f"Received: {data.decode('utf-8', errors='ignore')}") + + +try: + ser = serial.Serial(PORT, BAUDRATE, timeout=1) + print(f"Connected to {PORT}") + + # Запускаем чтение в фоне + thread = threading.Thread(target=read_from_port, args=(ser,), daemon=True) + thread.start() + + # Пишем данные + counter = 0 + while True: + msg = f"Ping {counter}\n" + ser.write(msg.encode("utf-8")) + print(f"Sent: {msg.strip()}") + counter += 1 + time.sleep(1) + +except serial.SerialException as e: + print(f"Error: {e}") +except KeyboardInterrupt: + print("\nExiting...") + if "ser" in locals() and ser.is_open: + ser.close()