commit 2cde55d4018f7b4adb296025420279df75a32b44 Author: Iwwww Date: Wed Dec 24 19:49:58 2025 +0300 Initial commit 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 0000000..89282b0 Binary files /dev/null and b/firmware/Build/stm32-usb-freertos.bin differ diff --git a/firmware/Drivers/CMSIS/Core b/firmware/Drivers/CMSIS/Core new file mode 160000 index 0000000..b7487de --- /dev/null +++ b/firmware/Drivers/CMSIS/Core @@ -0,0 +1 @@ +Subproject commit b7487de1303e2eb77c402432e368691e9dcfb5f0 diff --git a/firmware/Drivers/CMSIS/Device/ST/STM32F1xx b/firmware/Drivers/CMSIS/Device/ST/STM32F1xx new file mode 160000 index 0000000..c8e9a4a --- /dev/null +++ b/firmware/Drivers/CMSIS/Device/ST/STM32F1xx @@ -0,0 +1 @@ +Subproject commit c8e9a4a4f16b6d2cb2a2083cbe5161025280fb22 diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 0000000..d91b393 --- /dev/null +++ b/firmware/Makefile @@ -0,0 +1,103 @@ +TARGET = stm32-usb-freertos +BUILD_DIR = Build + +# --- Исходники --- +# 1. Приложение +C_SOURCES = \ +App/Src/main.c + +# 2. FreeRTOS +C_SOURCES += \ +Middlewares/FreeRTOS/croutine.c \ +Middlewares/FreeRTOS/event_groups.c \ +Middlewares/FreeRTOS/list.c \ +Middlewares/FreeRTOS/queue.c \ +Middlewares/FreeRTOS/tasks.c \ +Middlewares/FreeRTOS/timers.c \ +Middlewares/FreeRTOS/portable/GCC/ARM_CM3/port.c \ +Middlewares/FreeRTOS/portable/MemMang/heap_4.c + +# 3. TinyUSB +# Базовые файлы +C_SOURCES += \ +Middlewares/TinyUSB/src/tusb.c \ +Middlewares/TinyUSB/src/common/tusb_fifo.c \ +Middlewares/TinyUSB/src/device/usbd.c \ +Middlewares/TinyUSB/src/device/usbd_control.c \ +Middlewares/TinyUSB/src/class/cdc/cdc_device.c +# Драйвер для STM32 (Portable) +C_SOURCES += \ +Middlewares/TinyUSB/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c + +# 4. Startup +ASM_SOURCES = App/Src/startup_stm32f103xb.s + +# --- Настройки компилятора --- +PREFIX = arm-none-eabi- +CC = $(PREFIX)gcc +AS = $(PREFIX)gcc -x assembler-with-cpp +CP = $(PREFIX)objcopy +SZ = $(PREFIX)size + +MCU = -mcpu=cortex-m3 -mthumb -DSTM32F103xB + +# Includes +C_INCLUDES = \ +-IApp/Inc \ +-IDrivers/CMSIS/Core/Include \ +-IDrivers/CMSIS/Device/ST/STM32F1xx/Include \ +-IMiddlewares/FreeRTOS/include \ +-IMiddlewares/FreeRTOS/portable/GCC/ARM_CM3 \ +-IMiddlewares/TinyUSB/src + +# TinyUSB config specific defines +C_DEFS = \ +-DSTM32F103xB \ +-DCFG_TUSB_MCU=OPT_MCU_STM32F1 + +CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) -O2 -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 + +# Linker +LDSCRIPT = stm32f103c8.ld +LIBS = -lc -lm -lnosys +LDFLAGS = $(MCU) -T$(LDSCRIPT) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections + +# --- Правила сборки --- +all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin + +# Компиляция C +$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) + @mkdir -p $(dir $@) + $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ + +# Компиляция ASM +$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) + @mkdir -p $(dir $@) + $(AS) -c $(CFLAGS) $< -o $@ + +# Линковка +$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + $(SZ) $@ + +$(BUILD_DIR)/%.hex: $(BUILD_DIR)/$(TARGET).elf | $(BUILD_DIR) + $(CP) -O ihex $< $@ + +$(BUILD_DIR)/%.bin: $(BUILD_DIR)/$(TARGET).elf | $(BUILD_DIR) + $(CP) -O binary -S $< $@ + +$(BUILD_DIR): + mkdir $@ + +# Генерация списка объектов (автоматически из C_SOURCES) +OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) +vpath %.c $(sort $(dir $(C_SOURCES))) +OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o))) +vpath %.s $(sort $(dir $(ASM_SOURCES))) + +clean: + rm -rf $(BUILD_DIR) + +flash: + st-flash write $(BUILD_DIR)/$(TARGET).bin 0x8000000 + diff --git a/firmware/Middlewares/FreeRTOS b/firmware/Middlewares/FreeRTOS new file mode 160000 index 0000000..a9cb459 --- /dev/null +++ b/firmware/Middlewares/FreeRTOS @@ -0,0 +1 @@ +Subproject commit a9cb459206e605a90e7bcf38bf95d301277c5562 diff --git a/firmware/Middlewares/TinyUSB b/firmware/Middlewares/TinyUSB new file mode 160000 index 0000000..3af1bec --- /dev/null +++ b/firmware/Middlewares/TinyUSB @@ -0,0 +1 @@ +Subproject commit 3af1bec1a9161ee8dec29487831f7ac7ade9e189 diff --git a/firmware/stm32f103c8.ld b/firmware/stm32f103c8.ld new file mode 100644 index 0000000..d617a4d --- /dev/null +++ b/firmware/stm32f103c8.ld @@ -0,0 +1,149 @@ +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ + +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Memories definition */ +MEMORY +{ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K +} + +/* Sections */ +SECTIONS +{ + /* The startup code into "FLASH" Rom type memory */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* 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()