├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Kconfig ├── LICENSE ├── Makefile ├── README.md ├── arch ├── defconfig_emulation ├── defconfig_mega2560 ├── defconfig_stm32f103 ├── emulation │ ├── CMakeLists.txt │ ├── Makefile │ ├── arch-defs.h │ ├── config.h │ └── main.c ├── mega2560 │ ├── Makefile │ ├── arch-defs.h │ ├── config.h │ ├── flash.sh │ ├── main.c │ ├── platform.c │ ├── platform.h │ ├── steppers.c │ ├── steppers.h │ ├── uart.c │ └── uart.h ├── stm32f103 │ ├── CMakeLists.txt │ ├── Makefile │ ├── arch-defs.h │ ├── config.h │ ├── flash.sh │ ├── gdb-launch.sh │ ├── iface.c │ ├── iface.h │ ├── main.c │ ├── ocd-launch.sh │ ├── platform.c │ ├── platform.h │ ├── steppers.c │ ├── steppers.h │ ├── stm32.ld │ ├── toolchain.cmake │ ├── uart.c │ └── uart.h └── stm32f302 │ ├── FreeRTOSConfig.h │ ├── Makefile │ ├── arch-defs.h │ ├── clock-arch.h │ ├── config.h │ ├── flash.sh │ ├── gdb-launch.sh │ ├── iface.c │ ├── iface.h │ ├── libopencm3 │ ├── main.c │ ├── ocd-launch.sh │ ├── platform.c │ ├── platform.h │ ├── steppers.c │ ├── steppers.h │ ├── stm32.ld │ ├── uart.c │ ├── uart.h │ └── uip-conf.h ├── core ├── CMakeLists.txt ├── Makefile ├── control │ ├── CMakeLists.txt │ ├── commands │ │ ├── CMakeLists.txt │ │ ├── gcode_handler │ │ │ ├── CMakeLists.txt │ │ │ ├── gcode_handler.c │ │ │ └── gcode_handler.h │ │ └── status │ │ │ ├── CMakeLists.txt │ │ │ ├── print_status.c │ │ │ └── print_status.h │ ├── control.c │ ├── control.h │ ├── ioqueue │ │ ├── CMakeLists.txt │ │ ├── print_events.c │ │ └── print_events.h │ ├── moves │ │ ├── CMakeLists.txt │ │ ├── moves.c │ │ ├── moves.h │ │ ├── moves_arc │ │ │ ├── CMakeLists.txt │ │ │ ├── arc.c │ │ │ └── arc.h │ │ ├── moves_common │ │ │ ├── CMakeLists.txt │ │ │ ├── Makefile │ │ │ ├── acceleration.c │ │ │ ├── acceleration.h │ │ │ ├── common.c │ │ │ ├── common.h │ │ │ └── steppers.h │ │ ├── moves_line │ │ │ ├── CMakeLists.txt │ │ │ ├── line.c │ │ │ └── line.h │ │ └── unit │ │ │ ├── CMakeLists.txt │ │ │ ├── test_arc.c │ │ │ └── test_line.c │ ├── planner │ │ ├── CMakeLists.txt │ │ ├── planner.c │ │ └── planner.h │ ├── system.c │ ├── system.h │ └── tools │ │ ├── CMakeLists.txt │ │ ├── tools.c │ │ └── tools.h ├── defs.h ├── err │ ├── CMakeLists.txt │ └── err.h ├── gcode │ ├── CMakeLists.txt │ ├── gcodes.c │ └── gcodes.h ├── output │ ├── CMakeLists.txt │ ├── output.c │ └── output.h └── unit_tests │ ├── CMakeLists.txt │ ├── test_gcode.c │ ├── test_lines.c │ └── test_planner.c ├── debugutils └── sender.py ├── drivers └── Makefile ├── include ├── config │ ├── auto.conf │ └── auto.conf.cmd └── generated │ └── autoconf.h ├── libmodbus ├── Makefile ├── modbus.c └── modbus.h ├── libs ├── Makefile └── libshell │ ├── Makefile │ ├── shell.c │ └── shell.h ├── main └── Makefile └── util └── echo-uart.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.bin 4 | *.swp 5 | *.elf 6 | *.su 7 | .kdev4 8 | build 9 | .vscode 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libopencm3"] 2 | path = arch/stm32f103/libopencm3 3 | url = https://github.com/libopencm3/libopencm3 4 | [submodule "drivers/ethernet_enc28j60"] 5 | path = drivers/ethernet_enc28j60 6 | url = https://github.com/nort-cnc-control/ethernet_enc28j60 7 | [submodule "kbuild-standalone"] 8 | path = kbuild-standalone 9 | url = https://github.com/WangNan0/kbuild-standalone 10 | [submodule "libs/FreeRTOS-Plus-TCP"] 11 | path = libs/FreeRTOS-Plus-TCP 12 | url = https://github.com/FreeRTOS/FreeRTOS-Plus-TCP 13 | [submodule "libs/FreeRTOS-Kernel"] 14 | path = libs/FreeRTOS-Kernel 15 | url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git 16 | [submodule "libs/libip"] 17 | path = libs/libip 18 | url = git@github.com:nort-cnc-control/libip.git 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(controller) 4 | 5 | if (NOT DEFINED BUILD_TYPE) 6 | set(BUILD_TYPE "stm32f103") 7 | endif () 8 | 9 | message("Build type = ${BUILD_TYPE}") 10 | 11 | #if (${BUILD_TYPE} STREQUAL "stm32f103") 12 | # message("CROSS COMPILING") 13 | # set(CMAKE_C_COMPILER "arm-none-eabi-gcc") 14 | # set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") 15 | # set(CMAKE_C_FLAGS "") 16 | #else() 17 | # message("Test build") 18 | #endif() 19 | 20 | add_subdirectory(arch/${BUILD_TYPE}) 21 | add_subdirectory(core) 22 | 23 | add_subdirectory(drivers/ethernet_enc28j60/src/) 24 | 25 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | mainmenu "CNCControl_RT Configuration" 2 | 3 | menu "Compilation" 4 | 5 | config PROTECT_STACK 6 | bool 7 | 8 | config COPY_COMMAND 9 | bool 10 | default n 11 | 12 | config TOOLCHAIN_PREFIX 13 | string "cross toolchain prefix" 14 | default "" 15 | help 16 | "Cross toolchain prefix" 17 | 18 | choice COMPILE_OPTIMIZATION 19 | prompt "Optimization" 20 | default COMPILE_OPTIMIZATION_NONE 21 | config COMPILE_OPTIMIZATION_NONE 22 | bool "-O0" 23 | 24 | config COMPILE_OPTIMIZATION_FOR_SIZE 25 | bool "-Os" 26 | endchoice 27 | 28 | choice COMPILE_STACK_PROTECTION 29 | prompt "Stack protection" 30 | 31 | default COMPILE_STACK_PROTECTION_NONE 32 | 33 | config COMPILE_STACK_PROTECTION_NONE 34 | bool "-fno-stack-protector" 35 | 36 | config COMPILE_STACK_PROTECTION_ALL 37 | bool "-fstack-protector-all" 38 | select PROTECT_STACK 39 | 40 | endchoice 41 | 42 | config COMPILE_DEBUG 43 | bool "Include debug information" 44 | default y 45 | 46 | endmenu 47 | 48 | choice 49 | prompt "Platform select" 50 | default PLATFORM_EMULATION 51 | 52 | config PLATFORM_MEGA2560 53 | bool "mega2560" 54 | help 55 | arduino mega2560 board 56 | 57 | config PLATFORM_STM32F302 58 | bool "stm32f302" 59 | help 60 | stm32f302 board 61 | 62 | config PLATFORM_STM32F103 63 | bool "stm32f103" 64 | help 65 | stm32f103 board 66 | 67 | config PLATFORM_EMULATION 68 | bool "emulation" 69 | help 70 | emulate realtime board on linux 71 | 72 | endchoice 73 | 74 | menu "Board config" 75 | 76 | config UART 77 | bool 78 | 79 | config ETHERNET 80 | bool 81 | 82 | config IP 83 | bool 84 | 85 | config UDP 86 | bool 87 | 88 | config TCP 89 | bool 90 | 91 | config SPI 92 | bool 93 | 94 | 95 | if PLATFORM_EMULATION 96 | 97 | choice 98 | prompt "Control port" 99 | config BOARD_EMULATION_CONTROL_TCP 100 | bool "TCP" 101 | select TCP 102 | help 103 | Send commands via TCP 104 | endchoice 105 | 106 | config EMULATE_ENDSTOPS 107 | bool "Emulate endstops at 0, 0, 0" 108 | default n 109 | 110 | endif 111 | 112 | if PLATFORM_STM32F103 113 | 114 | choice 115 | prompt "Control port" 116 | default BOARD_STM32F103_CONTROL_UDP 117 | 118 | config BOARD_STM32F103_CONTROL_UDP 119 | bool "UDP" 120 | select ETHERNET 121 | select IP 122 | select UDP 123 | help 124 | Send commands via UDP 125 | 126 | endchoice 127 | 128 | endif 129 | 130 | if PLATFORM_STM32F302 131 | 132 | choice 133 | prompt "Control port" 134 | default BOARD_STM32F302_CONTROL_UDP 135 | 136 | config BOARD_STM32F302_CONTROL_UDP 137 | bool "UDP" 138 | select ETHERNET 139 | select IP 140 | select UDP 141 | help 142 | Send commands via UDP 143 | 144 | endchoice 145 | 146 | endif 147 | 148 | 149 | if PLATFORM_MEGA2560 150 | choice 151 | prompt "Control port" 152 | default BOARD_MEGA2560_CONTROL_UART 153 | 154 | config BOARD_MEGA2560_CONTROL_UART 155 | bool "UART" 156 | select UART 157 | select COPY_COMMAND 158 | help 159 | Send commands via UART 160 | 161 | endchoice 162 | 163 | endif 164 | 165 | endmenu 166 | 167 | menu "Drivers" 168 | if ETHERNET 169 | 170 | choice 171 | prompt "Ethenet device" 172 | default ETHERNET_DEVICE_ENC28J60 173 | 174 | config ETHERNET_DEVICE_ENC28J60 175 | bool "enc28j60" 176 | select SPI 177 | help 178 | Enc28j60 SPI ethernet driver 179 | 180 | endchoice 181 | endif 182 | endmenu 183 | 184 | menu "Communication" 185 | if ETHERNET 186 | 187 | config ETHERNET_MAC_ADDR 188 | string "MAC address" 189 | default "0C:00:00:00:00:02" 190 | help 191 | Ethernet device mac address 192 | 193 | endif 194 | 195 | if IP 196 | config IP_IPADDR 197 | string "Ip address" 198 | default "10.55.1.120" 199 | help 200 | Device IP address 201 | endif 202 | 203 | if UDP 204 | config UDP_PORT 205 | int "UDP port" 206 | default 8889 207 | help 208 | Device control UDP port 209 | endif 210 | 211 | if TCP 212 | config TCP_PORT 213 | int "TCP port" 214 | default 8889 215 | help 216 | Device control TCP port 217 | endif 218 | 219 | if UART 220 | endif 221 | 222 | config SHELL_RING_LEN 223 | int "Shell ring length" 224 | default 8 225 | help 226 | Amount of places in output ring buffer 227 | 228 | config SHELL_MSG_LEN 229 | int "Shell msg length" 230 | default 100 231 | help 232 | Max length of output messages 233 | endmenu 234 | 235 | config LIBCORE 236 | bool "Core support" 237 | default y 238 | help 239 | Support of CNC movement 240 | 241 | if LIBCORE 242 | menu "Core configuration" 243 | 244 | config QUEUE_SIZE 245 | int "commands queue size" 246 | default 10 247 | 248 | endmenu 249 | 250 | endif 251 | 252 | config LIBMODBUS 253 | bool "Modbus support" 254 | default y 255 | select UART 256 | help 257 | Support of modbus master 258 | 259 | 260 | if LIBMODBUS 261 | menu "Modbus configuration" 262 | 263 | endmenu 264 | endif 265 | 266 | config ECHO 267 | bool "Echo server for connection test" 268 | default n 269 | help 270 | Send "ECHO:" for test connection with board 271 | 272 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include .config 2 | export 3 | 4 | PREFIX = $(patsubst "%",%,$(CONFIG_TOOLCHAIN_PREFIX)) 5 | 6 | CC = $(PREFIX)gcc 7 | #CC = clang 8 | AR = $(PREFIX)ar 9 | OBJCOPY = $(PREFIX)objcopy 10 | OBJDUMP = $(PREFIX)objdump 11 | LD = $(PREFIX)ld 12 | 13 | ifdef CONFIG_PLATFORM_EMULATION 14 | PLATFORM := emulation 15 | endif 16 | 17 | ifdef CONFIG_PLATFORM_MEGA2560 18 | PLATFORM := mega2560 19 | CC += -mmcu=atmega2560 20 | endif 21 | 22 | ifdef CONFIG_PLATFORM_STM32F103 23 | PLATFORM := stm32f103 24 | CC += -fdata-sections -ffunction-sections -march=armv7-m -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -mfloat-abi=soft 25 | endif 26 | 27 | ifdef CONFIG_PLATFORM_STM32F302 28 | PLATFORM := stm32f302 29 | CC += -fdata-sections -ffunction-sections -march=armv7e-m+fp -mthumb -mcpu=cortex-m4 -mfloat-abi=hard 30 | endif 31 | 32 | ifdef CONFIG_COMPILE_OPTIMIZATION_NONE 33 | CC += -O0 34 | endif 35 | 36 | ifdef CONFIG_COMPILE_OPTIMIZATION_FOR_SIZE 37 | CC += -Os 38 | endif 39 | 40 | ifdef CONFIG_PROTECT_STACK 41 | CC += -Wl,--wrap=__stack_chk_fail 42 | CC += -fstack-usage 43 | endif 44 | 45 | ifdef CONFIG_COMPILE_STACK_PROTECTION_ALL 46 | CC += -fstack-protector-all 47 | endif 48 | 49 | ifdef CONFIG_COMPILE_STACK_PROTECTION_NONE 50 | CC += -fno-stack-protector 51 | endif 52 | 53 | ifdef CONFIG_COMPILE_DEBUG 54 | CC += -g -ggdb 55 | endif 56 | 57 | ROOT := $(shell pwd) 58 | export ROOT 59 | 60 | CC += -I$(ROOT)/arch/$(PLATFORM)/ 61 | 62 | export CC 63 | 64 | all: build_board 65 | 66 | TARGETS := 67 | 68 | ifdef CONFIG_LIBCORE 69 | build_libcore: 70 | $(MAKE) -C core/ 71 | TARGETS += build_libcore 72 | endif 73 | 74 | ifdef CONFIG_LIBMODBUS 75 | build_libmodbus: 76 | $(MAKE) -C libmodbus/ 77 | TARGETS += build_libmodbus 78 | endif 79 | 80 | build_drivers: drivers/ 81 | $(MAKE) -C drivers/ 82 | TARGETS += build_drivers 83 | 84 | build_libs: libs/ 85 | $(MAKE) -C libs/ 86 | TARGETS += build_libs 87 | 88 | build_board: $(TARGETS) 89 | $(MAKE) -C arch/$(PLATFORM) 90 | 91 | flash: 92 | $(MAKE) -C arch/$(PLATFORM) flash 93 | 94 | clean: 95 | $(MAKE) -C core clean 96 | $(MAKE) -C libmodbus clean 97 | $(MAKE) -C drivers clean 98 | $(MAKE) -C libs clean 99 | $(MAKE) -C arch/$(PLATFORM) clean 100 | 101 | menuconfig: 102 | mconf Kconfig 103 | 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is firmware for my stm32 based controller, which executes realtime part of the code for CNC. It supports simplified G-Code commands, and expected to run in pair with control program (https://github.com/nort-cnc-control/NoRTCNCControl). 4 | 5 | # Getting sources and building 6 | 7 | ``` 8 | git clone git@github.com:nort-cnc-control/cnccontrol_rt.git 9 | cd cnccontrol_rt 10 | git submodule init 11 | git submodule sync 12 | git submodule update 13 | cd kbuild-standalone 14 | mkdir build 15 | cd build 16 | make -C ../ -f Makefile.sample O=`pwd` 17 | cd kconfig 18 | export PATH=$PATH:`pwd` 19 | cd ../../../ 20 | ``` 21 | Then select configuration by copying config from `arc/defconfig_*` to `.config`, then run 22 | 23 | ``` 24 | make menuconfig 25 | make 26 | ``` 27 | ## Real hardware 28 | and `make flash` if you compile for real hardware (not emulation). avrdude for arduino or ocd for stm32 is required for `make flash`. 29 | 30 | ## Emulation 31 | ``` 32 | cd arch/emulation 33 | ./controller.elf 34 | ``` 35 | 36 | # Supported features 37 | 38 | ## Hardware 39 | - Board: stm32f103 'blue pill' and arduino mega2560 + ramps 40 | - Movements: 3-axis movements X, Y, Z. 41 | - Detectors: XYZ endstops, Probe 42 | - Communication: ethernet through enc28j60 (stm32), serial port (arduino) 43 | 44 | 45 | ## RT Commands 46 | 47 | ### Command format 48 | 49 | ``` 50 | RT: Nn Gg.... 51 | ``` 52 | 53 | Line numbering (N) is mandatory. 54 | 55 | ``` 56 | N0 G0 X10 F100 57 | ``` 58 | 59 | ### Supported commands 60 | #### Line movement 61 | ``` 62 | G0/G1 XxxYyyZzz Ffff Tttt Pppp Llll 63 | X, Y, Z - relative coordinates, steps, default=0 64 | F - feed, mm/sec, default=5/60 65 | P - initial feed, mm/sec, default=0 66 | L - finish feed, mm/sec, default=0 67 | T - acceleration, mm/sec^2, default=50 68 | ``` 69 | 70 | #### Helix movement 71 | ``` 72 | G2/G3 XxxYyyRrrSssHhhDdd G17/G18/G19 Aaaa Baaa Ffff Tttt Pppp Llll 73 | X, Y - end coordinates in system of center of helix, steps. 74 | R, S - start coordinates in system of center of helix, steps. 75 | H - height of helix, steps 76 | D - length of helix, mm 77 | A, B - axises of ellipse, steps 78 | G17/G18/G19 - selected plane 79 | F, T, P, L - same as for G0/G1 80 | ``` 81 | 82 | Attention: cnccontrol_rt assumes that XYZ are right-handed basis. If it is wrong, you need to exchange G2 and G3 in g-code commands 83 | 84 | #### Get/set current state 85 | 86 | - M3 - start tool 87 | - M5 - stop tool 88 | - M114 - current coordinates 89 | - M119 - endstops and Z-probe status 90 | - M800 - unlock movements 91 | - M801 - lock movements, =True on start 92 | - M802 - disable fail on endstops touch 93 | - M803 - enable fail on endstop touch, =True on start 94 | - M995 - disable break on probe 95 | - M996 - enable break on probe 96 | - M997 - set current posiiton to 0, 0, 0 97 | 98 | #### Reboot 99 | 100 | - M999 - reboot 101 | 102 | #### Using tool 103 | 104 | - M3 - enable tool 105 | - M5 - disable tool 106 | 107 | This section is for tools that required to start and stop while moving, for example lasers 108 | 109 | # Searching endstops and probing 110 | 111 | ## Searching endstops (simplified) 112 | 113 | ``` 114 | RT: N0 G0 X-30000 F50 T40 115 | RT: N1 M998 116 | RT: N2 G0 Y-30000 F50 T40 117 | RT: N3 M998 118 | RT: N4 G0 Z-30000 F50 T40 119 | RT: N5 M998 120 | RT: N6 M997 121 | ``` 122 | 123 | ## Probing 124 | 125 | Example sequence for probing. (Axis Z directed to bottom) 126 | ``` 127 | RT: N0 M996 128 | RT: N1 G0 Z20000 F500 T40 129 | RT: N2 M998 130 | RT: N3 G0 Z-1000 F100 T40 131 | RT: N4 G0 Z2000 F50 T40 132 | RT: N5 M998 133 | RT: N6 M995 134 | ``` 135 | 136 | # Modbus commands 137 | 138 | It is also supported modbus master for control spindel, light, cooling, etc. 139 | 140 | ``` 141 | MB:XXXX:YYYY:ZZZZ 142 | ``` 143 | 144 | where XXXX - devic number, YYYY - port number and ZZZZ - value 145 | 146 | # Default ports usage for stm32f103 blue pill 147 | 148 | - X-step - PC14 149 | - X-dir - PC15 150 | - Y-step - PA0 151 | - Y-dir - PA1 152 | - Z-step - PA2 153 | - Z-dir - PA3 154 | - X-endstop - PB7 155 | - Y-endstop- PB6 156 | - Z-endstop - PB5 157 | 158 | - Probe - Pb8 159 | - Tool - PB1 160 | 161 | # License 162 | 163 | GPLv3, see LICENSE file for full text 164 | -------------------------------------------------------------------------------- /arch/defconfig_emulation: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # CNCControl_RT Configuration 4 | # 5 | 6 | # 7 | # Compilation 8 | # 9 | CONFIG_TOOLCHAIN_PREFIX="" 10 | CONFIG_COMPILE_OPTIMIZATION_NONE=y 11 | # CONFIG_COMPILE_OPTIMIZATION_FOR_SIZE is not set 12 | CONFIG_COMPILE_STACK_PROTECTION_NONE=y 13 | # CONFIG_COMPILE_STACK_PROTECTION_ALL is not set 14 | CONFIG_COMPILE_DEBUG=y 15 | # end of Compilation 16 | 17 | # CONFIG_PLATFORM_MEGA2560 is not set 18 | # CONFIG_PLATFORM_STM32F103 is not set 19 | CONFIG_PLATFORM_EMULATION=y 20 | 21 | # 22 | # Board config 23 | # 24 | CONFIG_UART=y 25 | CONFIG_TCP=y 26 | CONFIG_BOARD_EMULATION_CONTROL_TCP=y 27 | # end of Board config 28 | 29 | # 30 | # Drivers 31 | # 32 | # end of Drivers 33 | 34 | # 35 | # Communication 36 | # 37 | CONFIG_TCP_PORT=8889 38 | # end of Communication 39 | 40 | CONFIG_LIBCORE=y 41 | 42 | # 43 | # Core configuration 44 | # 45 | CONFIG_LIBMODBUS=y 46 | 47 | # 48 | # Modbus configuration 49 | # 50 | -------------------------------------------------------------------------------- /arch/defconfig_mega2560: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # CNCControl_RT Configuration 4 | # 5 | 6 | # 7 | # Compilation 8 | # 9 | CONFIG_COPY_COMMAND=y 10 | CONFIG_TOOLCHAIN_PREFIX="avr-" 11 | # CONFIG_COMPILE_OPTIMIZATION_NONE is not set 12 | CONFIG_COMPILE_OPTIMIZATION_FOR_SIZE=y 13 | CONFIG_COMPILE_STACK_PROTECTION_NONE=y 14 | # CONFIG_COMPILE_STACK_PROTECTION_ALL is not set 15 | # CONFIG_COMPILE_DEBUG is not set 16 | # end of Compilation 17 | 18 | CONFIG_PLATFORM_MEGA2560=y 19 | # CONFIG_PLATFORM_STM32F302 is not set 20 | # CONFIG_PLATFORM_STM32F103 is not set 21 | # CONFIG_PLATFORM_EMULATION is not set 22 | 23 | # 24 | # Board config 25 | # 26 | CONFIG_UART=y 27 | CONFIG_BOARD_MEGA2560_CONTROL_UART=y 28 | # end of Board config 29 | 30 | # 31 | # Drivers 32 | # 33 | # end of Drivers 34 | 35 | # 36 | # Communication 37 | # 38 | CONFIG_SHELL_RING_LEN=8 39 | CONFIG_SHELL_MSG_LEN=100 40 | # end of Communication 41 | 42 | CONFIG_LIBCORE=y 43 | 44 | # 45 | # Core configuration 46 | # 47 | CONFIG_QUEUE_SIZE=10 48 | # end of Core configuration 49 | 50 | # CONFIG_LIBMODBUS is not set 51 | CONFIG_ECHO=y 52 | -------------------------------------------------------------------------------- /arch/defconfig_stm32f103: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # CNCControl_RT Configuration 4 | # 5 | 6 | # 7 | # Compilation 8 | # 9 | CONFIG_TOOLCHAIN_PREFIX="arm-none-eabi-" 10 | # CONFIG_COMPILE_OPTIMIZATION_NONE is not set 11 | CONFIG_COMPILE_OPTIMIZATION_FOR_SIZE=y 12 | # CONFIG_COMPILE_DEBUG is not set 13 | # end of Compilation 14 | 15 | # CONFIG_PLATFORM_MEGA2560 is not set 16 | CONFIG_PLATFORM_STM32F103=y 17 | # CONFIG_PLATFORM_EMULATION is not set 18 | 19 | # 20 | # Board config 21 | # 22 | CONFIG_UART=y 23 | CONFIG_ETHERNET=y 24 | CONFIG_IP=y 25 | CONFIG_UDP=y 26 | CONFIG_SPI=y 27 | CONFIG_BOARD_STM32F103_CONTROL_UDP=y 28 | # end of Board config 29 | 30 | # 31 | # Drivers 32 | # 33 | CONFIG_ETHERNET_DEVICE_ENC28J60=y 34 | # end of Drivers 35 | 36 | # 37 | # Communication 38 | # 39 | CONFIG_ETHERNET_MAC_ADDR="0C:00:00:00:00:02" 40 | CONFIG_IP_IPADDR="10.55.1.120" 41 | CONFIG_UDP_PORT=8889 42 | # end of Communication 43 | 44 | CONFIG_LIBCORE=y 45 | 46 | # 47 | # Core configuration 48 | # 49 | CONFIG_LIBMODBUS=y 50 | 51 | # 52 | # Modbus configuration 53 | # 54 | -------------------------------------------------------------------------------- /arch/emulation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | set(CMAKE_C_FLAGS "-O0 -DTEST_BUILD") 4 | set(CMAKE_C_FLAGS "-O0 -DTEST_BUILD" PARENT_SCOPE) 5 | 6 | add_executable(test main.c) 7 | target_link_libraries(test control err gcode m pthread rt output) 8 | 9 | -------------------------------------------------------------------------------- /arch/emulation/Makefile: -------------------------------------------------------------------------------- 1 | mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 2 | mkfile_dir := $(dir $(mkfile_path)) 3 | 4 | DEFS := 5 | 6 | ifdef CONFIG_BOARD_EMULATION_CONTROL_TCP 7 | DEFS += -DCONFIG_TCP_PORT=$(CONFIG_TCP_PORT) 8 | endif 9 | 10 | ifdef CONFIG_LIBCORE 11 | CC += -I$(ROOT)/core/ 12 | LIBCORE := $(ROOT)/core/libcore.a 13 | endif 14 | 15 | ifdef CONFIG_PROTECT_STACK 16 | CC += -DCONFIG_PROTECT_STACK 17 | endif 18 | 19 | ifdef CONFIG_EMULATE_ENDSTOPS 20 | CC += -DCONFIG_EMULATE_ENDSTOPS=true 21 | else 22 | CC += -DCONFIG_EMULATE_ENDSTOPS=false 23 | endif 24 | 25 | SRCS := main.c 26 | 27 | PWD = $(shell pwd) 28 | 29 | CC += -I$(PWD) 30 | 31 | OBJS := $(SRCS:%.c=%.o) 32 | 33 | all : controller.elf 34 | 35 | controller.elf: $(OBJS) 36 | $(CC) $(OBJS) $(LIBCORE) -lm -lpthread -o $@ 37 | 38 | %.o : %.c 39 | $(CC) -c $< -o $@ $(DEFS) 40 | 41 | clean: 42 | rm -f $(OBJS) controller.elf 43 | 44 | -------------------------------------------------------------------------------- /arch/emulation/arch-defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | -------------------------------------------------------------------------------- /arch/emulation/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DEBUG 0 4 | #define SHELL_BAUDRATE 9600 5 | 6 | -------------------------------------------------------------------------------- /arch/emulation/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | static void print_pos(void); 23 | 24 | 25 | int dsteps[3] = {0, 0, 0}; 26 | int steps[3]; 27 | double pos[3]; 28 | bool run = false; 29 | 30 | static void set_dir(int coord, bool dir) 31 | { 32 | if (dir == true) 33 | dsteps[coord] = 1; 34 | else 35 | dsteps[coord] = -1; 36 | } 37 | 38 | static void make_step(int coord) 39 | { 40 | steps[coord] += dsteps[coord]; 41 | pos[coord] = steps[coord] / moves_common_def.steps_per_unit[coord]; 42 | } 43 | 44 | static cnc_endstops get_stops(void) 45 | { 46 | cnc_endstops stops = { 47 | .stop_x = (pos[0] <= 0) && CONFIG_EMULATE_ENDSTOPS, 48 | .stop_y = (pos[1] <= 0) && CONFIG_EMULATE_ENDSTOPS, 49 | .stop_z = (pos[2] <= 0) && CONFIG_EMULATE_ENDSTOPS, 50 | .probe = 0, 51 | }; 52 | 53 | return stops; 54 | } 55 | 56 | bool line_st; 57 | pthread_t tid_tick; /* идентификатор потока */ 58 | 59 | #ifdef CONFIG_PROTECT_STACK 60 | void __wrap___stack_chk_fail(void) 61 | { 62 | printf("Stack protection failed!\n"); 63 | exit(0); 64 | } 65 | #endif 66 | 67 | static void* make_tick(void *arg) 68 | { 69 | while (line_st) 70 | { 71 | int delay_us = moves_step_tick(); 72 | if (delay_us <= 0) 73 | { 74 | break; 75 | } 76 | usleep(delay_us); 77 | // print_pos(); 78 | } 79 | return NULL; 80 | } 81 | 82 | static void line_started(void) 83 | { 84 | printf("Line started\n"); 85 | print_pos(); 86 | line_st = true; 87 | pthread_create(&tid_tick, NULL, make_tick, NULL); 88 | } 89 | 90 | static void line_finished(void) 91 | { 92 | printf("Line finished\n"); 93 | line_st = false; 94 | pthread_join(tid_tick, NULL); 95 | print_pos(); 96 | } 97 | 98 | static void line_error(void) 99 | { 100 | printf("Line error\n"); 101 | line_st = false; 102 | pthread_join(tid_tick, NULL); 103 | print_pos(); 104 | } 105 | 106 | 107 | static void print_pos(void) 108 | { 109 | int x, y, z; 110 | x = position.pos[0]; 111 | y = position.pos[1]; 112 | z = position.pos[2]; 113 | 114 | printf("Position = %i %i %i\n", x, y, z); 115 | } 116 | 117 | static void reboot(void) 118 | { 119 | planner_lock(); 120 | steps[0] = 0; 121 | steps[1] = 0; 122 | steps[2] = 0; 123 | pos[0] = 0; 124 | pos[1] = 0; 125 | pos[2] = 0; 126 | moves_reset(); 127 | output_control_write("Hello", -1); 128 | printf("Reboot\n"); 129 | } 130 | 131 | static void set_gpio(int i, int on) 132 | { 133 | if (on) 134 | printf("Tool %i in on\n", i); 135 | else 136 | printf("Tool %i is off\n", i); 137 | } 138 | 139 | void config_steppers(steppers_definition *sd, gpio_definition *gd) 140 | { 141 | sd->reboot = reboot; 142 | sd->set_dir = set_dir; 143 | sd->make_step = make_step; 144 | sd->get_endstops = get_stops; 145 | sd->line_started = line_started; 146 | sd->line_finished = line_finished; 147 | sd->line_error = line_error; 148 | gd->set_gpio = set_gpio; 149 | } 150 | 151 | static void init_steppers(void) 152 | { 153 | gpio_definition gd; 154 | steppers_definition sd = {}; 155 | config_steppers(&sd, &gd); 156 | init_control(&sd, &gd); 157 | } 158 | 159 | void test_init(void) 160 | { 161 | pos[0] = 0; 162 | pos[1] = 0; 163 | pos[2] = 0; 164 | steps[0] = 0; 165 | steps[1] = 0; 166 | steps[2] = 0; 167 | } 168 | 169 | pthread_mutex_t mutex; 170 | static int fd; 171 | 172 | void *receive(void *arg) 173 | { 174 | static char buf[1000]; 175 | ssize_t blen = 0; 176 | 177 | printf("Starting recv thread. fd = %i\n", fd); 178 | while (run) 179 | { 180 | unsigned char b; 181 | ssize_t n = read(fd, &b, 1); 182 | 183 | 184 | if (n < 1) 185 | { 186 | printf("Disconnected\n"); 187 | run = false; 188 | break; 189 | } 190 | if (b == '\n' || b == '\r') 191 | { 192 | if (blen >= 3 && !memcmp(buf, "RT:", 3)) 193 | { 194 | pthread_mutex_lock(&mutex); 195 | printf("Execute: %.*s\n", (int)(blen - 3), buf + 3); 196 | const unsigned char *cmd = buf + 3; 197 | ssize_t cmdlen = blen - 3; 198 | execute_g_command(cmd, cmdlen); 199 | pthread_mutex_unlock(&mutex); 200 | } 201 | else if (blen >= 5 && !memcmp(buf, "EXIT:", 5)) 202 | { 203 | run = false; 204 | } 205 | else if (blen >= 5 && !memcmp(buf, "ECHO:", 5)) 206 | { 207 | output_control_write(buf, blen); 208 | } 209 | blen = 0; 210 | memset(buf, 0, sizeof(buf)); 211 | } 212 | else 213 | { 214 | buf[blen++] = b; 215 | } 216 | } 217 | return NULL; 218 | } 219 | 220 | static ssize_t write_fun(int fd, const void *data, ssize_t len) 221 | { 222 | printf("Send line: %.*s\n", (int)len, (const char*)data); 223 | if (fd == 0) 224 | { 225 | write(fd, data, len); 226 | write(fd, "\n", 1); 227 | } 228 | else 229 | { 230 | write(fd, data, len); 231 | write(fd, "\n", 1); 232 | } 233 | return 0; 234 | } 235 | 236 | static int create_control(unsigned short port) 237 | { 238 | struct sockaddr_in ctl_sockaddr; 239 | int ctlsock = socket(AF_INET, SOCK_STREAM, 0); 240 | setsockopt(ctlsock, SOL_SOCKET, SO_REUSEADDR, NULL, 0); 241 | 242 | ctl_sockaddr.sin_family = AF_INET; 243 | ctl_sockaddr.sin_port = htons(port); 244 | ctl_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 245 | 246 | if (bind(ctlsock, (struct sockaddr *)&ctl_sockaddr, sizeof(ctl_sockaddr)) < 0) 247 | { 248 | printf("Can not bind tcp\n"); 249 | return -1; 250 | } 251 | listen(ctlsock, 1); 252 | return ctlsock; 253 | } 254 | 255 | int main(int argc, const char **argv) 256 | { 257 | pthread_t tid_rcv; /* идентификатор потока */ 258 | const int port = CONFIG_TCP_PORT; 259 | 260 | int sock = create_control(port); 261 | if (sock <= 0) 262 | { 263 | printf("Error opening\n"); 264 | return 0; 265 | } 266 | 267 | printf("Listening control on :%i\n", port); 268 | pthread_mutex_init(&mutex, NULL); 269 | 270 | while (true) 271 | { 272 | fd = accept(sock, NULL, NULL); 273 | run = true; 274 | printf("Connect from client\n"); 275 | 276 | output_control_set_fd(fd); 277 | output_shell_set_fd(0); 278 | output_set_write_fun(write_fun); 279 | 280 | test_init(); 281 | init_steppers(); 282 | 283 | pthread_create(&tid_rcv, NULL, receive, &fd); 284 | usleep(100000); 285 | output_control_write("Hello", -1); 286 | 287 | planner_lock(); 288 | moves_reset(); 289 | 290 | while (run) 291 | { 292 | pthread_mutex_lock(&mutex); 293 | planner_pre_calculate(); 294 | pthread_mutex_unlock(&mutex); 295 | planner_report_states(); 296 | usleep(1000); 297 | } 298 | 299 | close(fd); 300 | } 301 | pthread_mutex_destroy(&mutex); 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /arch/mega2560/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_PORT="/dev/ttyACM0" 2 | 3 | DEFS := 4 | 5 | SRCS := main.c \ 6 | platform.c 7 | 8 | CC += -I$(ROOT)/libs/libshell/ 9 | LIBS += $(ROOT)/libs/libshell/libshell.a 10 | 11 | ifdef CONFIG_LIBCORE 12 | CC += -I$(ROOT)/core/ -DCONFIG_LIBCORE 13 | LIBS += $(ROOT)/core/libcore.a 14 | SRCS += steppers.c 15 | endif 16 | 17 | ifdef CONFIG_LIBMODBUS 18 | CC += -I$(ROOT)/libmodbus/ -DCONFIG_LIBMODBUS 19 | LIBS += $(ROOT)/libmodbus/libmodbus.a 20 | endif 21 | 22 | ifdef CONFIG_BOARD_MEGA2560_CONTROL_UART 23 | CC += -DCONFIG_BOARD_MEGA2560_CONTROL_UART 24 | endif 25 | 26 | ifdef CONFIG_UART 27 | CC += -DCONFIG_UART 28 | SRCS += uart.c 29 | endif 30 | 31 | OBJS := $(SRCS:%.c=%.o) 32 | SUS := $(SRCS:%.c=%.su) 33 | 34 | PWD := $(shell pwd) 35 | 36 | CC += -I$(PWD) -DF_CPU=16000000UL 37 | 38 | LDFLAGS := 39 | 40 | all : controller.elf controller.bin 41 | 42 | %.o : %.c 43 | $(CC) -c $< -o $@ 44 | 45 | controller.bin: controller.elf 46 | $(OBJCOPY) -O binary $< $@ 47 | 48 | controller.elf: $(OBJS) 49 | $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -lm -o $@ 50 | 51 | flash: controller.bin 52 | # avrdude -v -patmega2560 -carduino -P/dev/ttyACM0 -b115200 -D -Uflash:w:$< 53 | avrdude -v -patmega2560 -cwiring -P"$(PROGRAM_PORT)" -b115200 -D -Uflash:w:$< 54 | # avrdude -v -patmega2560 -cusbasp -b9600 -D -Uflash:w:$< 55 | 56 | clean: 57 | rm -f $(OBJS) controller.bin controller.elf $(SUS) 58 | 59 | -------------------------------------------------------------------------------- /arch/mega2560/arch-defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef long int ssize_t; 4 | 5 | -------------------------------------------------------------------------------- /arch/mega2560/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SHELL_BAUDRATE 38400 4 | #define LOCK_ON_CRC_ERROR 0 5 | 6 | /* X configure */ 7 | #define X_STEP_PORT F 8 | #define X_STEP_PIN 0 9 | 10 | #define X_DIR_PORT F 11 | #define X_DIR_PIN 1 12 | 13 | #define X_EN_PORT D 14 | #define X_EN_PIN 7 15 | 16 | #define X_MIN_PORT E 17 | #define X_MIN_PIN 5 18 | 19 | #define X_MAX_PORT D 20 | #define X_MAX_PIN 1 21 | 22 | /* Y configure */ 23 | #define Y_STEP_PORT F 24 | #define Y_STEP_PIN 6 25 | 26 | #define Y_DIR_PORT F 27 | #define Y_DIR_PIN 7 28 | 29 | #define Y_EN_PORT F 30 | #define Y_EN_PIN 2 31 | 32 | #define Y_MIN_PORT J 33 | #define Y_MIN_PIN 1 34 | 35 | #define Y_MAX_PORT J 36 | #define Y_MAX_PIN 0 37 | 38 | /* Z configure */ 39 | #define Z_STEP_PORT L 40 | #define Z_STEP_PIN 3 41 | 42 | #define Z_DIR_PORT L 43 | #define Z_DIR_PIN 1 44 | 45 | #define Z_EN_PORT K 46 | #define Z_EN_PIN 0 47 | 48 | #define Z_MIN_PORT D 49 | #define Z_MIN_PIN 3 50 | 51 | #define Z_MAX_PORT D 52 | #define Z_MAX_PIN 2 53 | 54 | /* Probe configure */ 55 | #define PROBE_PORT K 56 | #define PROBE_PIN 1 57 | 58 | /* GPIO tool 0 - TABLE HEATER/LASER - Digital pin 8 */ 59 | #define TOOL0_PORT H 60 | #define TOOL0_PIN 5 61 | 62 | /* GPIO tool 1 - PS ON */ 63 | #define TOOL1_PORT B 64 | #define TOOL1_PIN 6 65 | 66 | /* Indicator LED */ 67 | #define LED_PORT B 68 | #define LED_PIN 7 69 | 70 | /* Modbus UART */ 71 | #define MODBUS_UART_PORT 2 72 | #define MODBUS_UART_BAUDRATE 9600UL 73 | #define MODBUS_UART_RTS_PORT A 74 | #define MODBUS_UART_RTS_PIN 1 75 | 76 | /* Control UART */ 77 | #define CONTROL_UART_PORT 0 78 | #define CONTROL_UART_BAUDRATE 38400UL 79 | 80 | #define CONCAT2(a, b) (a##b) 81 | #define CONCAT3(a, b, c) (a##b##c) 82 | 83 | #define DDR(x) CONCAT2(DDR, x) 84 | #define PORT(x) CONCAT2(PORT, x) 85 | #define PIN(x) CONCAT2(PIN, x) 86 | 87 | #define gpio_get(port, pin) ((PIN(port) & (1 << (pin))) >> (pin)) 88 | #define gpio_set(port, pin) (PORT(port) |= (1 << (pin))) 89 | #define gpio_clear(port, pin) (PORT(port) &= ~(1 << (pin))) 90 | 91 | 92 | -------------------------------------------------------------------------------- /arch/mega2560/flash.sh: -------------------------------------------------------------------------------- 1 | #avrdude -c wiring -p atmega2560 -U flash:w:controller.bin -P /dev/ttyACM0 -B 115200 2 | #avrdude -c usbasp -p atmega2560 -U flash:w:controller.bin 3 | avrdude -v -patmega2560 -cwiring -P/dev/ttyACM0 -b115200 -D -Uflash:w:controller.bin 4 | 5 | -------------------------------------------------------------------------------- /arch/mega2560/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "config.h" 3 | 4 | #ifdef CONFIG_LIBCORE 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "steppers.h" 10 | #endif 11 | 12 | #include 13 | 14 | #include "platform.h" 15 | 16 | #ifdef CONFIG_LIBCORE 17 | static void init_steppers(void) 18 | { 19 | gpio_definition gd; 20 | 21 | steppers_definition sd = {}; 22 | steppers_config(&sd, &gd); 23 | init_control(&sd, &gd); 24 | } 25 | #endif 26 | 27 | /* main */ 28 | 29 | 30 | int main(void) 31 | { 32 | hardware_setup(); 33 | 34 | #ifdef CONFIG_LIBCORE 35 | init_steppers(); 36 | planner_lock(); 37 | moves_reset(); 38 | #endif 39 | 40 | shell_add_message("Hello", -1); 41 | 42 | while (true) 43 | { 44 | #ifdef CONFIG_LIBCORE 45 | planner_pre_calculate(); 46 | planner_report_states(); 47 | #endif 48 | hardware_loop(); 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /arch/mega2560/platform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "platform.h" 4 | #include 5 | 6 | #include 7 | 8 | #ifdef CONFIG_LIBCORE 9 | #include "steppers.h" 10 | #endif 11 | 12 | #ifdef CONFIG_UART 13 | #include "uart.h" 14 | #endif 15 | 16 | #ifdef CONFIG_LIBMODBUS 17 | static void modbus_uart_send(const uint8_t *data, size_t len) 18 | { 19 | uart_send_modbus(data, len); 20 | } 21 | #else 22 | static void (*modbus_uart_send)(const uint8_t *data, size_t len) = NULL; 23 | #endif 24 | 25 | void hardware_setup(void) 26 | { 27 | sei(); 28 | #ifdef CONFIG_UART 29 | uart_setup(); 30 | #endif 31 | 32 | #ifdef CONFIG_LIBCORE 33 | steppers_setup(); 34 | #endif 35 | 36 | shell_setup(NULL, modbus_uart_send); 37 | } 38 | 39 | void hardware_loop(void) 40 | { 41 | #ifdef CONFIG_BOARD_MEGA2560_CONTROL_UART 42 | if (uart_message_received) 43 | { 44 | uart_message_received = false; 45 | uart_rxie_enable(false); 46 | shell_data_completed(); 47 | uart_rxie_enable(true); 48 | } 49 | 50 | ssize_t len; 51 | const uint8_t* data = shell_pick_message(&len); 52 | if (data != NULL) 53 | { 54 | uart_send_control(data, len); 55 | uart_send_control("\n\r", 2); 56 | shell_send_completed(); 57 | } 58 | #endif 59 | } 60 | 61 | -------------------------------------------------------------------------------- /arch/mega2560/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void hardware_setup(void); 4 | 5 | void hardware_loop(void); 6 | 7 | void hard_fault_handler(void); 8 | 9 | -------------------------------------------------------------------------------- /arch/mega2560/steppers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | #include "steppers.h" 10 | 11 | #define PSC 64 12 | #define FTIMER (F_CPU / PSC) 13 | #define TIMEOUT_TIMER_STEP 1000UL 14 | 15 | static void set_gpio(int id, int on); 16 | 17 | static bool going = false; 18 | 19 | static void steppers_timer_setup(void) 20 | { 21 | TCCR1A = 0 << WGM11 | 0 << WGM10; 22 | TCCR1B = 0 << WGM13 | 1 << WGM12; 23 | TCCR1C = 0; 24 | TIMSK1 |= 1 << OCIE1B | 1 << OCIE1A; 25 | OCR1B = 3; 26 | } 27 | 28 | void steppers_setup(void) 29 | { 30 | steppers_timer_setup(); 31 | 32 | DDR(X_STEP_PORT) |= 1 << X_STEP_PIN; 33 | DDR(X_DIR_PORT) |= 1 << X_DIR_PIN; 34 | DDR(X_EN_PORT) |= 1 << X_EN_PIN; // X 35 | 36 | DDR(Y_STEP_PORT) |= 1 << Y_STEP_PIN; 37 | DDR(Y_DIR_PORT) |= 1 << Y_DIR_PIN; 38 | DDR(Y_EN_PORT) |= 1 << Y_EN_PIN; // Y 39 | 40 | DDR(Z_STEP_PORT) |= 1 << Z_STEP_PIN; 41 | DDR(Z_DIR_PORT) |= 1 << Z_DIR_PIN; 42 | DDR(Z_EN_PORT) |= 1 << Z_EN_PIN; // Z 43 | 44 | // X - stop 45 | DDR(X_MIN_PORT) &= ~(1 << X_MIN_PIN); 46 | PORT(X_MIN_PORT) |= 1 << X_MIN_PIN; 47 | DDR(X_MAX_PORT) &= ~(1 << X_MAX_PIN); 48 | PORT(X_MAX_PORT) |= 1 << X_MAX_PIN; 49 | 50 | // Y - stop 51 | DDR(Y_MIN_PORT) &= ~(1 << Y_MIN_PIN); 52 | PORT(Y_MIN_PORT) |= 1 << Y_MIN_PIN; 53 | DDR(Y_MAX_PORT) &= ~(1 << Y_MAX_PIN); 54 | PORT(Y_MAX_PORT) |= 1 << Y_MAX_PIN; 55 | 56 | // Y - stop 57 | DDR(Z_MIN_PORT) &= ~(1 << Z_MIN_PIN); 58 | PORT(Z_MIN_PORT) |= 1 << Z_MIN_PIN; 59 | DDR(Z_MAX_PORT) &= ~(1 << Z_MAX_PIN); 60 | PORT(Z_MAX_PORT) |= 1 << Z_MAX_PIN; 61 | 62 | // Probe 63 | DDR(PROBE_PORT) &= ~(1 << PROBE_PIN); 64 | PORT(PROBE_PORT) |= 1 << PROBE_PIN; 65 | 66 | // GPIO Tool 0 67 | set_gpio(0, false); 68 | DDR(TOOL0_PORT) |= 1 << TOOL0_PIN; // TABLE HEATER / LASER 69 | 70 | // GPIO Tool 1 71 | set_gpio(1, false); 72 | DDR(TOOL1_PORT) |= 1 << TOOL1_PIN; // PS ON 73 | 74 | // Indication LED 75 | DDR(LED_PORT) |= 1 << LED_PIN; 76 | PORT(LED_PORT) &= ~(1 << LED_PIN); 77 | } 78 | 79 | static volatile int moving; 80 | 81 | static void enable_steppers(bool en) 82 | { 83 | if (en) 84 | { 85 | PORT(X_EN_PORT) &= ~(1< 65535) 160 | delay = 65535; 161 | OCR1A = delay; 162 | TCNT1 = 0; 163 | } 164 | 165 | ISR(TIMER1_COMPA_vect) 166 | { 167 | if (!going) 168 | { 169 | TCCR1B &= ~(1 << CS12 | 1 << CS11 | 1 << CS10); 170 | return; 171 | } 172 | 173 | // next step of movement 174 | // it can set STEP pins active (low) 175 | make_tick(); 176 | } 177 | 178 | ISR(TIMER1_COMPB_vect) 179 | { 180 | // set STEP pins not active (low) at the end of STEP 181 | // ___ 182 | // ______| |_______ 183 | // 184 | // ^ 185 | // | 186 | // here 187 | end_step(); 188 | } 189 | 190 | static void line_started(void) 191 | { 192 | // PB7 has LED. Enable it 193 | gpio_set(LED_PORT, LED_PIN); 194 | 195 | // Set initial STEP state 196 | end_step(); 197 | moving = 1; 198 | 199 | // first tick 200 | going = true; 201 | make_tick(); 202 | if (going) 203 | { 204 | TCNT1 = 0; 205 | TCCR1B |= (0 << CS12 | 1 << CS11 | 1 << CS10); 206 | } 207 | } 208 | 209 | static void line_finished(void) 210 | { 211 | TCCR1B &= ~(1 << CS12 | 1 << CS11 | 1 << CS10); 212 | going = false; 213 | 214 | // disable LED 215 | gpio_clear(LED_PORT, LED_PIN); 216 | 217 | // initial STEP state 218 | end_step(); 219 | moving = 0; 220 | } 221 | 222 | static void line_error(void) 223 | { 224 | // temporary do same things as in finished case 225 | TCCR1B &= ~(1 << CS12 | 1 << CS11 | 1 << CS10); 226 | going = false; 227 | 228 | gpio_set(LED_PORT, LED_PIN); 229 | end_step(); 230 | moving = 0; 231 | } 232 | 233 | static void set_gpio(int id, int on) 234 | { 235 | switch (id) 236 | { 237 | case 0: 238 | if (on) 239 | gpio_set(TOOL0_PORT, TOOL0_PIN); 240 | else 241 | gpio_clear(TOOL0_PORT, TOOL0_PIN); 242 | break; 243 | case 1: 244 | if (on) 245 | gpio_clear(TOOL1_PORT, TOOL1_PIN); 246 | else 247 | gpio_set(TOOL1_PORT, TOOL1_PIN); 248 | break; 249 | } 250 | } 251 | 252 | static cnc_endstops get_stops(void) 253 | { 254 | cnc_endstops stops = { 255 | .stop_x = !(gpio_get(X_MIN_PORT, X_MIN_PIN)), 256 | .stop_y = !(gpio_get(Y_MIN_PORT, Y_MIN_PIN)), 257 | .stop_z = !(gpio_get(Z_MIN_PORT, Z_MIN_PIN)), 258 | .probe = !(gpio_get(PROBE_PORT, PROBE_PIN)), 259 | }; 260 | 261 | return stops; 262 | } 263 | 264 | static void reboot(void) 265 | { 266 | cli(); //irq's off 267 | wdt_enable(WDTO_15MS); //wd on,15ms 268 | while(1); //loop 269 | } 270 | 271 | void steppers_config(steppers_definition *sd, gpio_definition *gd) 272 | { 273 | sd->reboot = reboot; 274 | sd->set_dir = set_dir; 275 | sd->make_step = make_step; 276 | sd->enable_step = enable_steppers; 277 | sd->get_endstops = get_stops; 278 | sd->line_started = line_started; 279 | sd->line_finished = line_finished; 280 | sd->line_error = line_error; 281 | gd->set_gpio = set_gpio; 282 | } 283 | 284 | -------------------------------------------------------------------------------- /arch/mega2560/steppers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void steppers_setup(void); 7 | 8 | void steppers_config(steppers_definition *sd, gpio_definition *gd); 9 | 10 | -------------------------------------------------------------------------------- /arch/mega2560/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | #include 10 | #include 11 | 12 | #define UDR(x) CONCAT2(UDR, x) 13 | #define UCSRA(x) CONCAT3(UCSR, x, A) 14 | #define UCSRB(x) CONCAT3(UCSR, x, B) 15 | #define UCSRC(x) CONCAT3(UCSR, x, C) 16 | 17 | #define UBRRL(x) CONCAT3(UBRR, x, L) 18 | #define UBRRH(x) CONCAT3(UBRR, x, H) 19 | 20 | #define TXC(x) CONCAT2(TXC, x) 21 | #define UDRE(x) CONCAT2(UDRE, x) 22 | #define RXEN(x) CONCAT2(RXEN, x) 23 | #define TXEN(x) CONCAT2(TXEN, x) 24 | #define UCSZ2(x) CONCAT2(UCSZ2, x) 25 | #define UCSZ1(x) CONCAT2(UCSZ1, x) 26 | #define UCSZ0(x) CONCAT2(UCSZ0, x) 27 | 28 | #define RXCIE(x) CONCAT2(RXCIE, x) 29 | #define TXCIE(x) CONCAT2(TXCIE, x) 30 | 31 | #define USART_RX_vect(x) CONCAT3(USART, x, _RX_vect) 32 | 33 | #ifdef CONFIG_LIBMODBUS 34 | void uart_send_modbus(const uint8_t *data, size_t len) 35 | { 36 | /* RTS */ 37 | PORT(MODBUS_UART_RTS_PORT) &= ~(1 << MODBUS_UART_RTS_PIN); 38 | 39 | while (len > 0) 40 | { 41 | uint8_t d = *(data++); 42 | while (UCSRA(MODBUS_UART_PORT) & (1< 0) 58 | { 59 | uint8_t d = *data; 60 | while ((UCSRA(CONTROL_UART_PORT) & (1 << UDRE(CONTROL_UART_PORT))) == 0) 61 | ; 62 | UDR(CONTROL_UART_PORT) = d; 63 | len--; 64 | data++; 65 | } 66 | } 67 | 68 | void uart_rxie_enable(bool en) 69 | { 70 | if (en) 71 | UCSRB(CONTROL_UART_PORT) |= (1 << RXCIE(CONTROL_UART_PORT)); 72 | else 73 | UCSRB(CONTROL_UART_PORT) &= ~(1 << RXCIE(CONTROL_UART_PORT)); 74 | } 75 | 76 | ISR(USART0_RX_vect) 77 | { 78 | uint8_t c = UDR(CONTROL_UART_PORT); 79 | if (c == '\n' || c == '\r') 80 | { 81 | uart_message_received = true; 82 | } 83 | else 84 | { 85 | shell_data_received(&c, 1); 86 | } 87 | } 88 | 89 | #endif 90 | 91 | 92 | void uart_setup(void) 93 | { 94 | uint16_t ubrr; 95 | #ifdef CONFIG_LIBMODBUS 96 | UCSRA(MODBUS_UART_PORT) = 0; 97 | UCSRB(MODBUS_UART_PORT) = (1 << TXEN(MODBUS_UART_PORT)); 98 | // UCSRC(MODBUS_UART_PORT) = (1 << UCSZ1(MODBUS_UART_PORT)) | (1 << UCSZ0(MODBUS_UART_PORT)); 99 | 100 | ubrr = F_CPU / 16 / MODBUS_UART_BAUDRATE - 1; 101 | UBRRL(MODBUS_UART_PORT) = ubrr & 0xFF; 102 | UBRRH(MODBUS_UART_PORT) = (ubrr >> 8) & 0xFF; 103 | #endif 104 | 105 | #ifdef CONFIG_BOARD_MEGA2560_CONTROL_UART 106 | UCSRA(CONTROL_UART_PORT) = 0; 107 | UCSRB(CONTROL_UART_PORT) = (1 << TXEN(CONTROL_UART_PORT)) | (1 << RXEN(CONTROL_UART_PORT)) | (1 << RXCIE(CONTROL_UART_PORT)); 108 | // UCSRC(CONTROL_UART_PORT) = (1 << UCSZ1(CONTROL_UART_PORT)) | (1 << UCSZ0(CONTROL_UART_PORT)); 109 | 110 | ubrr = F_CPU / 16 / CONTROL_UART_BAUDRATE - 1; 111 | UBRRL(CONTROL_UART_PORT) = ubrr & 0xFF; 112 | UBRRH(CONTROL_UART_PORT) = (ubrr >> 8) & 0XFF; 113 | #endif 114 | 115 | } 116 | 117 | -------------------------------------------------------------------------------- /arch/mega2560/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void uart_setup(void); 8 | 9 | #ifdef CONFIG_LIBMODBUS 10 | void uart_send_modbus(const uint8_t *buf, size_t len); 11 | #endif 12 | 13 | #ifdef CONFIG_BOARD_MEGA2560_CONTROL_UART 14 | void uart_send_control(const uint8_t *buf, size_t len); 15 | extern bool uart_message_received; 16 | void uart_rxie_enable(bool en); 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /arch/stm32f103/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | 4 | set(LINKER_FLAGS "-T ${CMAKE_CURRENT_SOURCE_DIR}/stm32.ld -Wl,--gc-sections --static -nostartfiles -specs=nano.specs -specs=nosys.specs") 5 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 6 | set(CMAKE_EXE_LINKER_FLAGS "${LINKER_FLAGS}") 7 | 8 | include_directories(libopencm3/include/) 9 | link_directories(libopencm3/lib/) 10 | 11 | if (1) 12 | add_executable(controller.elf main.c stm32f103.spi.c steps.c uart.c) 13 | else() 14 | add_executable(controller.elf main.c stm32f103.eth.c steps.c uart.c) 15 | endif() 16 | 17 | target_link_libraries(controller.elf control err gcode output opencm3_stm32f1 m enc28j60) 18 | target_include_directories(controller.elf PUBLIC enc28j60) 19 | 20 | add_custom_target(controller.bin ALL arm-none-eabi-objcopy -O binary controller.elf controller.bin) 21 | add_dependencies(controller.bin controller.elf) 22 | 23 | -------------------------------------------------------------------------------- /arch/stm32f103/Makefile: -------------------------------------------------------------------------------- 1 | mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 2 | mkfile_dir := $(dir $(mkfile_path)) 3 | 4 | LD := $(CC) 5 | 6 | DEFS := 7 | 8 | SRCS := main.c \ 9 | platform.c \ 10 | iface.c 11 | 12 | CC += -I$(ROOT)/libs/libshell/ 13 | LIBS += $(ROOT)/libs/libshell/libshell.a 14 | 15 | ifdef CONFIG_PROTECT_STACK 16 | CC += -DCONFIG_PROTECT_STACK 17 | endif 18 | 19 | ifdef CONFIG_UDP 20 | CC += -DCONFIG_UDP_PORT=$(CONFIG_UDP_PORT) 21 | endif 22 | 23 | ifdef CONFIG_LIBCORE 24 | CC += -I$(ROOT)/core/ -DCONFIG_LIBCORE 25 | LIBS += $(ROOT)/core/libcore.a 26 | SRCS += steppers.c 27 | endif 28 | 29 | ifdef CONFIG_LIBMODBUS 30 | CC += -I$(ROOT)/libmodbus/ -DCONFIG_LIBMODBUS 31 | LIBS += $(ROOT)/libmodbus/libmodbus.a 32 | endif 33 | 34 | ifdef CONFIG_UART 35 | CC += -DCONFIG_UART 36 | SRCS += uart.c 37 | endif 38 | 39 | ifdef CONFIG_ETHERNET_DEVICE_ENC28J60 40 | CC += -I$(ROOT)/drivers/ethernet_enc28j60/include -DCONFIG_ETHERNET_DEVICE_ENC28J60 41 | LIBS += $(ROOT)/drivers/ethernet_enc28j60/libenc28j60.a 42 | endif 43 | 44 | ifdef CONFIG_IP 45 | CC += -DCONFIG_ETHERNET_MAC_ADDR=\"$(CONFIG_ETHERNET_MAC_ADDR)\" 46 | CC += -DCONFIG_IP_IPADDR=\"$(CONFIG_IP_IPADDR)\" 47 | CC += -I$(ROOT)/libs/libip -DCONFIG_IP 48 | LIBS += $(ROOT)/libs/libip/libip.a 49 | endif 50 | 51 | 52 | OBJS := $(SRCS:%.c=%.o) 53 | SUS := $(SRCS:%.c=%.su) 54 | 55 | PWD := $(shell pwd) 56 | 57 | CC += -I$(PWD) -I$(PWD)/libopencm3/include 58 | 59 | LDFLAGS := -T $(PWD)/stm32.ld -Wl,--gc-sections --static -nostartfiles -specs=nano.specs -specs=nosys.specs 60 | 61 | all : controller.elf controller.bin 62 | 63 | %.o : %.c 64 | $(CC) -c $< -o $@ 65 | 66 | controller.bin: controller.elf 67 | $(OBJCOPY) -O binary $< $@ 68 | 69 | controller.elf: libopencm3 $(OBJS) 70 | $(LD) $(LDFLAGS) $(OBJS) libopencm3/lib/libopencm3_stm32f1.a $(LIBS) -o $@ -lm 71 | 72 | flash: controller.bin 73 | openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "init; reset halt; flash write_image erase $< 0x08000000 bin; reset; exit" 74 | 75 | libopencm3: 76 | $(MAKE) -C $(mkfile_dir)/libopencm3/ 77 | 78 | clean: 79 | rm -f $(OBJS) controller.bin controller.elf $(SUS) 80 | 81 | -------------------------------------------------------------------------------- /arch/stm32f103/arch-defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | -------------------------------------------------------------------------------- /arch/stm32f103/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DEBUG 0 4 | #define SHELL_BAUDRATE 38400 5 | #define LOCK_ON_CRC_ERROR 0 6 | 7 | -------------------------------------------------------------------------------- /arch/stm32f103/flash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "init; reset halt; flash write_image erase $1 0x08000000 bin; reset; exit" 4 | -------------------------------------------------------------------------------- /arch/stm32f103/gdb-launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GDB=gdb-multiarch 4 | 5 | $GDB "$1" -ex 'target remote localhost:3333' 6 | -------------------------------------------------------------------------------- /arch/stm32f103/iface.c: -------------------------------------------------------------------------------- 1 | #define STM32F1 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include "iface.h" 10 | #include 11 | 12 | #include 13 | 14 | /* SPI */ 15 | #define SPI_RCC RCC_SPI2 16 | #define SPI_ID SPI2 17 | #define SPI_PORT GPIOB 18 | #define SPI_NSS GPIO12 19 | #define SPI_SCK GPIO13 20 | #define SPI_MISO GPIO14 21 | #define SPI_MOSI GPIO15 22 | 23 | /* Ethernet */ 24 | 25 | #define ETH_RST_PORT GPIOB 26 | #define ETH_RST_PIN GPIO11 27 | 28 | uint8_t spi_rw(uint8_t data) 29 | { 30 | spi_send(SPI_ID, data); 31 | uint8_t c = spi_read(SPI_ID); 32 | return c; 33 | } 34 | 35 | void spi_cs(bool enable) 36 | { 37 | if (enable) 38 | { 39 | gpio_clear(SPI_PORT, SPI_NSS); 40 | } 41 | else 42 | { 43 | gpio_set(SPI_PORT, SPI_NSS); 44 | } 45 | } 46 | 47 | void spi_write_buf(const uint8_t *data, size_t len) 48 | { 49 | while (len-- > 0) 50 | { 51 | spi_rw(*(data++)); 52 | } 53 | } 54 | 55 | void spi_read_buf(uint8_t *data, size_t len) 56 | { 57 | while (len-- > 0) 58 | { 59 | *(data++) = spi_rw(0xFF); 60 | } 61 | } 62 | 63 | void spi_setup(void) 64 | { 65 | int i; 66 | rcc_periph_clock_enable(SPI_RCC); 67 | 68 | for (i = 0; i < 100000; i++) 69 | asm("nop"); 70 | 71 | gpio_set_mode(SPI_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI | SPI_SCK); 72 | gpio_set_mode(SPI_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS); 73 | gpio_set_mode(SPI_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO); 74 | gpio_set(SPI_PORT, SPI_MISO); 75 | 76 | spi_reset(SPI_ID); 77 | 78 | spi_init_master(SPI_ID, 79 | SPI_CR1_BAUDRATE_FPCLK_DIV_8, 80 | SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, 81 | SPI_CR1_CPHA_CLK_TRANSITION_1, 82 | SPI_CR1_DFF_8BIT, 83 | SPI_CR1_MSBFIRST); 84 | spi_enable_software_slave_management(SPI_ID); 85 | spi_set_nss_high(SPI_ID); 86 | spi_enable(SPI_ID); 87 | } 88 | 89 | /* Network */ 90 | static struct enc28j60_state_s eth_state; 91 | static uint8_t eth_mac[6]; 92 | 93 | static void eth_hard_reset(bool rst) 94 | { 95 | if (rst) 96 | gpio_clear(ETH_RST_PORT, ETH_RST_PIN); 97 | else 98 | gpio_set(ETH_RST_PORT, ETH_RST_PIN); 99 | } 100 | 101 | void send_ethernet_frame(const uint8_t *payload, size_t payload_len) 102 | { 103 | enc28j60_send_data(ð_state, payload, payload_len); 104 | } 105 | 106 | static uint8_t eth_buf[1524]; 107 | static void netPoll(void) 108 | { 109 | bool received = enc28j60_has_package(ð_state); 110 | 111 | if (received) 112 | { 113 | uint32_t status, crc; 114 | ssize_t len = enc28j60_read_packet(ð_state, eth_buf, sizeof(eth_buf), &status, &crc); 115 | 116 | if (len > 0) 117 | { 118 | libip_handle_ethernet(eth_buf, len); 119 | } 120 | } 121 | } 122 | 123 | void ifaceInitialise(const uint8_t mac[6]) 124 | { 125 | memcpy(eth_mac, mac, 6); 126 | /* Init SPI2 */ 127 | spi_setup(); 128 | 129 | /* Configure hard reset pin */ 130 | gpio_set_mode(ETH_RST_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, ETH_RST_PIN); 131 | gpio_set(ETH_RST_PORT, ETH_RST_PIN); 132 | 133 | /* Init enc28j60 module */ 134 | enc28j60_init(ð_state, eth_hard_reset, spi_rw, spi_cs, spi_write_buf, spi_read_buf); 135 | bool res = enc28j60_configure(ð_state, eth_mac, 4096, true); 136 | } 137 | 138 | void ifacePoll(void) 139 | { 140 | netPoll(); 141 | } 142 | -------------------------------------------------------------------------------- /arch/stm32f103/iface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void ifaceInitialise(const uint8_t mac[6]); 8 | void ifacePoll(void); 9 | -------------------------------------------------------------------------------- /arch/stm32f103/main.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifdef CONFIG_LIBCORE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "steppers.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | 14 | #include "platform.h" 15 | #include 16 | #include 17 | 18 | 19 | #ifdef CONFIG_LIBCORE 20 | static void init_steppers(void) 21 | { 22 | gpio_definition gd; 23 | 24 | steppers_definition sd = {}; 25 | steppers_config(&sd, &gd); 26 | init_control(&sd, &gd); 27 | } 28 | #endif 29 | 30 | static struct 31 | { 32 | uint32_t ip; 33 | uint16_t port; 34 | } remote; 35 | 36 | void udp_packet_handler(uint32_t remote_ip, uint16_t dport, uint16_t sport, const uint8_t *payload, size_t len) 37 | { 38 | if (dport == CONFIG_UDP_PORT) 39 | { 40 | remote.ip = remote_ip; 41 | remote.port = sport; 42 | shell_data_received(payload, len); 43 | shell_data_completed(); 44 | } 45 | } 46 | 47 | void shellSend(void) 48 | { 49 | ssize_t len; 50 | const uint8_t *data = shell_pick_message(&len); 51 | if (data == NULL) 52 | return; 53 | libip_send_udp_packet(data, len, remote.ip, CONFIG_UDP_PORT, remote.port); 54 | shell_send_completed(); 55 | } 56 | 57 | static void net_setup(void) 58 | { 59 | const char *ipstr = CONFIG_IP_IPADDR; 60 | const char *macstr = CONFIG_ETHERNET_MAC_ADDR; 61 | 62 | unsigned mac[6]; 63 | unsigned ip[4]; 64 | sscanf(ipstr, "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]); 65 | sscanf(macstr, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); 66 | 67 | uint32_t ipaddr = (ip[0] << 24) | (ip[1] << 16) | (ip[2] << 8) | (ip[3]); 68 | uint8_t macaddr[6] = {(uint8_t)(mac[0]), (uint8_t)(mac[1]), (uint8_t)(mac[2]), (uint8_t)(mac[3]), (uint8_t)(mac[4]), (uint8_t)(mac[5])}; 69 | ifaceInitialise(macaddr); 70 | libip_init(ipaddr, macaddr); 71 | shell_setup(NULL, NULL); 72 | } 73 | 74 | /* main */ 75 | int main(void) 76 | { 77 | hardware_setup(); 78 | 79 | #ifdef CONFIG_LIBCORE 80 | init_steppers(); 81 | planner_lock(); 82 | moves_reset(); 83 | #endif 84 | net_setup(); 85 | 86 | shell_add_message("Hello", -1); 87 | 88 | while (true) 89 | { 90 | #ifdef CONFIG_LIBCORE 91 | planner_pre_calculate(); 92 | #endif 93 | ifacePoll(); 94 | shellSend(); 95 | } 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /arch/stm32f103/ocd-launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg 4 | -------------------------------------------------------------------------------- /arch/stm32f103/platform.c: -------------------------------------------------------------------------------- 1 | #define STM32F1 2 | 3 | #include "platform.h" 4 | #include "net.h" 5 | 6 | #include 7 | 8 | #ifdef CONFIG_SPI 9 | #include "spi.h" 10 | #endif 11 | 12 | #ifdef CONFIG_LIBCORE 13 | #include "steppers.h" 14 | #endif 15 | 16 | #ifdef CONFIG_UART 17 | #include "uart.h" 18 | #endif 19 | 20 | #ifdef CONFIG_ETHERNET_DEVICE_ENC28J60 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* RCC */ 30 | static void clock_setup(void) 31 | { 32 | rcc_clock_setup_in_hse_8mhz_out_72mhz(); 33 | 34 | /* Enable GPIOA clock. */ 35 | rcc_periph_clock_enable(RCC_GPIOA); 36 | rcc_periph_clock_enable(RCC_GPIOB); 37 | rcc_periph_clock_enable(RCC_GPIOC); 38 | 39 | /* Enable clocks for GPIO port A (for GPIO_USART1_TX) and USART1. */ 40 | rcc_periph_clock_enable(RCC_USART1); 41 | 42 | /* Enable TIM2 clock for steps*/ 43 | rcc_periph_clock_enable(RCC_TIM2); 44 | 45 | /* Enable TIM3 clock for connection timeouts */ 46 | rcc_periph_clock_enable(RCC_TIM3); 47 | 48 | #ifdef CONFIG_SPI 49 | /* Enable SPI2 */ 50 | rcc_periph_clock_enable(RCC_AFIO); 51 | rcc_periph_clock_enable(RCC_SPI2); 52 | 53 | /* Enable DMA1 */ 54 | rcc_periph_clock_enable(RCC_DMA1); 55 | #endif 56 | 57 | // Delay 58 | int i; 59 | for (i = 0; i < 100000; i++) 60 | __asm__("nop"); 61 | } 62 | 63 | static void gpio_setup(void) 64 | { 65 | /* Blink led */ 66 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_10_MHZ, 67 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); 68 | } 69 | 70 | void hardware_setup(void) 71 | { 72 | SCB_VTOR = (uint32_t) 0x08000000; 73 | 74 | clock_setup(); 75 | gpio_setup(); 76 | 77 | #ifdef CONFIG_SPI 78 | spi_setup(); 79 | #endif 80 | 81 | #ifdef CONFIG_UART 82 | uart_setup(9600); 83 | #endif 84 | 85 | #ifdef CONFIG_LIBCORE 86 | steppers_setup(); 87 | #endif 88 | 89 | shell_setup(NULL, uart_send); 90 | 91 | gpio_set(GPIOC, GPIO13); 92 | 93 | int i; 94 | for (i = 0; i < 0x400000; i++) 95 | __asm__("nop"); 96 | } 97 | 98 | void hardware_loop(void) 99 | { 100 | net_receive(); 101 | net_send(); 102 | } 103 | 104 | #ifdef CONFIG_PROTECT_STACK 105 | void __wrap___stack_chk_fail(void) 106 | { 107 | static int i; 108 | while (1) 109 | { 110 | for (i = 0; i < 0x40000; i++) 111 | __asm__("nop"); 112 | gpio_set(GPIOC, GPIO13); 113 | for (i = 0; i < 0x40000; i++) 114 | __asm__("nop"); 115 | gpio_clear(GPIOC, GPIO13); 116 | } 117 | } 118 | #endif 119 | 120 | void hard_fault_handler(void) 121 | { 122 | static int i; 123 | while (1) 124 | { 125 | for (i = 0; i < 0x400000; i++) 126 | __asm__("nop"); 127 | gpio_set(GPIOC, GPIO13); 128 | for (i = 0; i < 0x400000; i++) 129 | __asm__("nop"); 130 | gpio_clear(GPIOC, GPIO13); 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /arch/stm32f103/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void hardware_setup(void); 4 | 5 | void hardware_loop(void); 6 | 7 | void hard_fault_handler(void); 8 | 9 | -------------------------------------------------------------------------------- /arch/stm32f103/steppers.c: -------------------------------------------------------------------------------- 1 | #define STM32F1 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "config.h" 13 | #include "steppers.h" 14 | 15 | #define FCPU 72000000UL 16 | #define FTIMER 100000UL 17 | #define PSC ((FCPU) / (FTIMER) - 1) 18 | #define TIMEOUT_TIMER_STEP 1000UL 19 | 20 | static bool going = false; 21 | 22 | static void steppers_timer_setup(void) 23 | { 24 | rcc_periph_reset_pulse(RST_TIM2); 25 | 26 | timer_set_prescaler(TIM2, PSC); 27 | timer_direction_up(TIM2); 28 | timer_disable_preload(TIM2); 29 | timer_update_on_overflow(TIM2); 30 | timer_enable_update_event(TIM2); 31 | timer_continuous_mode(TIM2); 32 | 33 | timer_set_oc_fast_mode(TIM2, TIM_OC1); 34 | timer_set_oc_value(TIM2, TIM_OC1, 1); 35 | 36 | nvic_set_priority(NVIC_TIM2_IRQ, 0x00); 37 | 38 | nvic_enable_irq(NVIC_TIM2_IRQ); 39 | timer_enable_irq(TIM2, TIM_DIER_UIE); 40 | timer_enable_irq(TIM2, TIM_DIER_CC1IE); 41 | } 42 | 43 | void steppers_timer_irq_enable(bool en) 44 | { 45 | if (en) 46 | { 47 | nvic_enable_irq(NVIC_TIM2_IRQ); 48 | } 49 | else 50 | { 51 | nvic_disable_irq(NVIC_TIM2_IRQ); 52 | } 53 | } 54 | 55 | void steppers_setup(void) 56 | { 57 | steppers_timer_setup(); 58 | 59 | // X - step 60 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_10_MHZ, 61 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO14); 62 | // X - dir 63 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_10_MHZ, 64 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO15); 65 | 66 | // Y - step 67 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 68 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0); 69 | // Y - dir 70 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 71 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO1); 72 | 73 | // Z - step 74 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 75 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO2); 76 | // Z - dir 77 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 78 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO3); 79 | 80 | // Enable 81 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_10_MHZ, 82 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0); 83 | 84 | // X - stop 85 | gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO7); 86 | gpio_set(GPIOB, GPIO7); 87 | 88 | // Y - stop 89 | gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO6); 90 | gpio_set(GPIOB, GPIO6); 91 | 92 | // Z - stop 93 | gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO5); 94 | gpio_set(GPIOB, GPIO5); 95 | 96 | // Probe 97 | gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO8); 98 | gpio_set(GPIOB, GPIO8); 99 | 100 | // GPIO Tool 0 101 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, 102 | GPIO_CNF_OUTPUT_OPENDRAIN, GPIO1); 103 | } 104 | 105 | static volatile int moving; 106 | 107 | static void set_dir(int i, bool dir) 108 | { 109 | if (dir) { 110 | switch (i) { 111 | case 0: 112 | gpio_set(GPIOC, GPIO15); 113 | break; 114 | case 1: 115 | gpio_set(GPIOA, GPIO1); 116 | break; 117 | case 2: 118 | gpio_set(GPIOA, GPIO3); 119 | break; 120 | } 121 | } else { 122 | switch (i) { 123 | case 0: 124 | gpio_clear(GPIOC, GPIO15); 125 | break; 126 | case 1: 127 | gpio_clear(GPIOA, GPIO1); 128 | break; 129 | case 2: 130 | gpio_clear(GPIOA, GPIO3); 131 | break; 132 | } 133 | } 134 | } 135 | 136 | static void make_step(int i) 137 | { 138 | switch (i) 139 | { 140 | case 0: 141 | gpio_clear(GPIOC, GPIO14); 142 | break; 143 | case 1: 144 | gpio_clear(GPIOA, GPIO0); 145 | break; 146 | case 2: 147 | gpio_clear(GPIOA, GPIO2); 148 | break; 149 | } 150 | } 151 | 152 | static void end_step(void) 153 | { 154 | gpio_set(GPIOC, GPIO14); 155 | gpio_set(GPIOA, GPIO0); 156 | gpio_set(GPIOA, GPIO2); 157 | } 158 | 159 | static void make_tick(void) 160 | { 161 | int delay_us = moves_step_tick(); 162 | if (delay_us < 0) 163 | { 164 | return; 165 | } 166 | int delay = delay_us * FTIMER / 1000000UL; 167 | if (delay < 3) 168 | delay = 3; 169 | timer_set_counter(TIM2, 0); 170 | timer_set_period(TIM2, delay); 171 | } 172 | 173 | void tim2_isr(void) 174 | { 175 | if (!going) 176 | { 177 | TIM_SR(TIM2) &= ~TIM_SR_UIF; 178 | TIM_SR(TIM2) &= ~TIM_SR_CC1IF; 179 | 180 | timer_disable_counter(TIM2); 181 | return; 182 | } 183 | if (TIM_SR(TIM2) & TIM_SR_UIF) { 184 | // next step of movement 185 | // it can set STEP pins active (low) 186 | TIM_SR(TIM2) &= ~TIM_SR_UIF; 187 | make_tick(); 188 | } 189 | else if (TIM_SR(TIM2) & TIM_SR_CC1IF) { 190 | // set STEP pins not active (low) at the end of STEP 191 | // ___ 192 | // ______| |_______ 193 | // 194 | // ^ 195 | // | 196 | // here 197 | TIM_SR(TIM2) &= ~TIM_SR_CC1IF; 198 | end_step(); 199 | } 200 | } 201 | 202 | static void line_started(void) 203 | { 204 | // PC13 has LED. Enable it 205 | gpio_clear(GPIOC, GPIO13); 206 | 207 | // Set initial STEP state 208 | end_step(); 209 | moving = 1; 210 | 211 | // first tick 212 | going = true; 213 | make_tick(); 214 | if (going) 215 | timer_enable_counter(TIM2); 216 | } 217 | 218 | static void line_finished(void) 219 | { 220 | timer_disable_counter(TIM2); 221 | going = false; 222 | 223 | // disable LED 224 | gpio_set(GPIOC, GPIO13); 225 | 226 | // initial STEP state 227 | end_step(); 228 | moving = 0; 229 | } 230 | 231 | static void line_error(void) 232 | { 233 | // temporary do same things as in finished case 234 | timer_disable_counter(TIM2); 235 | going = false; 236 | 237 | gpio_set(GPIOC, GPIO13); 238 | end_step(); 239 | moving = 0; 240 | } 241 | 242 | static void set_gpio(int id, int on) 243 | { 244 | switch (id) 245 | { 246 | case 0: 247 | if (on) 248 | gpio_clear(GPIOB, GPIO1); 249 | else 250 | gpio_set(GPIOB, GPIO1); 251 | break; 252 | } 253 | } 254 | 255 | static cnc_endstops get_stops(void) 256 | { 257 | cnc_endstops stops = { 258 | .stop_x = !(gpio_get(GPIOB, GPIO7)), 259 | .stop_y = !(gpio_get(GPIOB, GPIO6)), 260 | .stop_z = !(gpio_get(GPIOB, GPIO5)), 261 | .probe = !(gpio_get(GPIOB, GPIO8)), 262 | }; 263 | 264 | return stops; 265 | } 266 | 267 | static void reboot(void) 268 | { 269 | SCB_AIRCR = (0x5FA<<16)|(1 << 2); 270 | for (;;) 271 | ; 272 | } 273 | 274 | void steppers_config(steppers_definition *sd, gpio_definition *gd) 275 | { 276 | sd->reboot = reboot; 277 | sd->set_dir = set_dir; 278 | sd->make_step = make_step; 279 | sd->get_endstops = get_stops; 280 | sd->line_started = line_started; 281 | sd->line_finished = line_finished; 282 | sd->line_error = line_error; 283 | gd->set_gpio = set_gpio; 284 | } 285 | 286 | -------------------------------------------------------------------------------- /arch/stm32f103/steppers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void steppers_setup(void); 7 | 8 | void steppers_config(steppers_definition *sd, gpio_definition *gd); 9 | 10 | -------------------------------------------------------------------------------- /arch/stm32f103/stm32.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K 4 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 5 | } 6 | 7 | INCLUDE libopencm3/lib/cortex-m-generic.ld 8 | 9 | -------------------------------------------------------------------------------- /arch/stm32f103/toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR ARM) 3 | 4 | if(MINGW OR CYGWIN OR WIN32) 5 | set(UTIL_SEARCH_CMD where) 6 | elseif(UNIX OR APPLE) 7 | set(UTIL_SEARCH_CMD which) 8 | endif() 9 | 10 | set(TOOLCHAIN_PREFIX arm-none-eabi-) 11 | 12 | execute_process( 13 | COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc 14 | OUTPUT_VARIABLE BINUTILS_PATH 15 | OUTPUT_STRIP_TRAILING_WHITESPACE 16 | ) 17 | 18 | get_filename_component(ARM_TOOLCHAIN_DIR ${BINUTILS_PATH} DIRECTORY) 19 | # Without that flag CMake is not able to pass test compilation check 20 | if (${CMAKE_VERSION} VERSION_EQUAL "3.6.0" OR ${CMAKE_VERSION} VERSION_GREATER "3.6") 21 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 22 | else() 23 | set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nosys.specs") 24 | endif() 25 | 26 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) 27 | set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) 28 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) 29 | 30 | set(CMAKE_OBJCOPY ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool") 31 | set(CMAKE_SIZE_UTIL ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool") 32 | 33 | set(CMAKE_SYSROOT ${ARM_TOOLCHAIN_DIR}/../arm-none-eabi) 34 | set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH}) 35 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 36 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 37 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 38 | 39 | set(CMAKE_C_FLAGS "-O0 -g -ggdb -fdata-sections -ffunction-sections -march=armv7-m -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -mfloat-abi=soft") 40 | set(CMAKE_CXX_FLAGS "-O0 -g -ggdb -fdata-sections -ffunction-sections -march=armv7-m -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -mfloat-abi=soft") 41 | -------------------------------------------------------------------------------- /arch/stm32f103/uart.c: -------------------------------------------------------------------------------- 1 | 2 | #define STM32F1 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "config.h" 13 | 14 | static volatile int i; 15 | void uart_send(const uint8_t *data, size_t len) 16 | { 17 | gpio_clear(GPIOA, GPIO12); 18 | for (i = 0; i < 100000; i++) 19 | __asm__("nop"); 20 | while (len > 0) 21 | { 22 | uint16_t d = *(data++); 23 | while ((USART_SR(USART1) & USART_SR_TXE) == 0) 24 | ; 25 | USART_DR(USART1) = d; 26 | // usart_send_blocking(USART1, d); 27 | len--; 28 | } 29 | for (i = 0; i < 100000; i++) 30 | __asm__("nop"); 31 | gpio_set(GPIOA, GPIO12); 32 | } 33 | 34 | void uart_setup(int baudrate) 35 | { 36 | // nvic_enable_irq(NVIC_USART1_IRQ); 37 | 38 | /* Setup GPIO pin GPIO_USART1_RE_RX on GPIO port A for receive. */ 39 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 40 | GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX); 41 | 42 | /* Setup GPIO pin GPIO_USART1_TX. */ 43 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 44 | GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); 45 | 46 | /* Setup GPIO pin GPIO_USART1_RTS. */ 47 | gpio_set(GPIOA, GPIO12); 48 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, 49 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); 50 | 51 | /* Setup UART parameters. */ 52 | usart_set_baudrate(USART1, baudrate); 53 | usart_set_databits(USART1, 8); 54 | usart_set_stopbits(USART1, USART_STOPBITS_1); 55 | usart_set_mode(USART1, USART_MODE_TX_RX); 56 | usart_set_parity(USART1, USART_PARITY_NONE); 57 | usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); 58 | 59 | USART_CR1(USART1) |= USART_CR1_RXNEIE; 60 | USART_CR1(USART1) |= USART_CR1_TCIE; 61 | 62 | /* Finally enable the USART. */ 63 | usart_enable(USART1); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /arch/stm32f103/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void uart_setup(int baudrate); 8 | 9 | void uart_send(const uint8_t *buf, size_t len); 10 | 11 | -------------------------------------------------------------------------------- /arch/stm32f302/FreeRTOSConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeRTOS V202012.00 3 | * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | * the Software, and to permit persons to whom the Software is furnished to do so, 10 | * subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | * 22 | * http://www.FreeRTOS.org 23 | * http://aws.amazon.com/freertos 24 | * 25 | * 1 tab == 4 spaces! 26 | */ 27 | 28 | 29 | #ifndef FREERTOS_CONFIG_H 30 | #define FREERTOS_CONFIG_H 31 | 32 | /*----------------------------------------------------------- 33 | * Application specific definitions. 34 | * 35 | * These definitions should be adjusted for your particular hardware and 36 | * application requirements. 37 | * 38 | * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE 39 | * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 40 | * 41 | * See http://www.freertos.org/a00110.html 42 | *----------------------------------------------------------*/ 43 | 44 | /* Ensure stdint is only used by the compiler, and not the assembler. */ 45 | 46 | #define configUSE_PREEMPTION 1 47 | #define configUSE_IDLE_HOOK 1 48 | #define configUSE_TICK_HOOK 1 49 | #define configCPU_CLOCK_HZ ( 72000000UL ) 50 | #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 51 | #define configMAX_PRIORITIES ( 5 ) 52 | #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 ) 53 | #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 16 * 1024 ) ) 54 | #define configMAX_TASK_NAME_LEN ( 10 ) 55 | #define configUSE_TRACE_FACILITY 1 56 | #define configUSE_16_BIT_TICKS 0 57 | #define configIDLE_SHOULD_YIELD 1 58 | #define configUSE_MUTEXES 1 59 | #define configQUEUE_REGISTRY_SIZE 8 60 | #define configCHECK_FOR_STACK_OVERFLOW 1 61 | #define configUSE_RECURSIVE_MUTEXES 1 62 | #define configUSE_MALLOC_FAILED_HOOK 1 63 | #define configUSE_APPLICATION_TASK_TAG 0 64 | #define configUSE_COUNTING_SEMAPHORES 1 65 | #define configGENERATE_RUN_TIME_STATS 0 66 | 67 | /* Co-routine definitions. */ 68 | #define configUSE_CO_ROUTINES 0 69 | #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) 70 | 71 | /* Software timer definitions. */ 72 | #define configUSE_TIMERS 1 73 | #define configTIMER_TASK_PRIORITY ( 2 ) 74 | #define configTIMER_QUEUE_LENGTH 10 75 | #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) 76 | 77 | /* Set the following definitions to 1 to include the API function, or zero 78 | to exclude the API function. */ 79 | #define INCLUDE_vTaskPrioritySet 1 80 | #define INCLUDE_uxTaskPriorityGet 1 81 | #define INCLUDE_vTaskDelete 1 82 | #define INCLUDE_vTaskCleanUpResources 1 83 | #define INCLUDE_vTaskSuspend 1 84 | #define INCLUDE_vTaskDelayUntil 1 85 | #define INCLUDE_vTaskDelay 1 86 | 87 | /* Cortex-M specific definitions. */ 88 | #ifdef __NVIC_PRIO_BITS 89 | /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ 90 | #define configPRIO_BITS __NVIC_PRIO_BITS 91 | #else 92 | #define configPRIO_BITS 4 /* 15 priority levels */ 93 | #endif 94 | 95 | /* The lowest interrupt priority that can be used in a call to a "set priority" 96 | function. */ 97 | #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf 98 | 99 | /* The highest interrupt priority that can be used by any interrupt service 100 | routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL 101 | INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER 102 | PRIORITY THAN THIS! (higher priorities are lower numeric values. */ 103 | #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 104 | 105 | /* Interrupt priorities used by the kernel port layer itself. These are generic 106 | to all Cortex-M ports, and do not rely on any particular library functions. */ 107 | #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) 108 | 109 | /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! 110 | See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ 111 | #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) 112 | 113 | /* Normal assert() semantics without relying on the provision of an assert.h 114 | header file. */ 115 | #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } 116 | 117 | /* Definitions that map the FreeRTOS port interrupt handlers to their libopencm3 standard names. */ 118 | #define vPortSVCHandler sv_call_handler 119 | #define xPortPendSVHandler pend_sv_handler 120 | #define xPortSysTickHandler sys_tick_handler 121 | 122 | 123 | #endif /* FREERTOS_CONFIG_H */ 124 | 125 | -------------------------------------------------------------------------------- /arch/stm32f302/Makefile: -------------------------------------------------------------------------------- 1 | mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 2 | mkfile_dir := $(dir $(mkfile_path)) 3 | 4 | LD := $(CC) 5 | 6 | DEFS := 7 | 8 | SRCS := main.c \ 9 | platform.c \ 10 | iface.c 11 | 12 | CC += -I$(ROOT)/libs/libshell/ 13 | LIBS += $(ROOT)/libs/libshell/libshell.a 14 | 15 | # ---------- FreeRTOS section ----------- 16 | FREERTOS_PATH := $(ROOT)/libs/FreeRTOS-Kernel 17 | FREERTOS_SRC := $(FREERTOS_PATH)/list.c \ 18 | $(FREERTOS_PATH)/queue.c \ 19 | $(FREERTOS_PATH)/tasks.c \ 20 | $(FREERTOS_PATH)/timers.c \ 21 | $(FREERTOS_PATH)/event_groups.c \ 22 | $(FREERTOS_PATH)/portable/MemMang/heap_4.c \ 23 | $(FREERTOS_PATH)/portable/GCC/ARM_CM4F/port.c 24 | SRCS += $(FREERTOS_SRC) 25 | CC += -I$(FREERTOS_PATH)/include 26 | CC += -I$(FREERTOS_PATH)/portable/GCC/ARM_CM4F 27 | # ---------- FreeRTOS section ------------ 28 | 29 | ifdef CONFIG_IP 30 | CC += -DCONFIG_ETHERNET_MAC_ADDR=\"$(CONFIG_ETHERNET_MAC_ADDR)\" 31 | CC += -DCONFIG_IP_IPADDR=\"$(CONFIG_IP_IPADDR)\" 32 | CC += -I$(ROOT)/libs/libip -DCONFIG_IP 33 | LIBS += $(ROOT)/libs/libip/libip.a 34 | endif 35 | 36 | ifdef CONFIG_UDP 37 | CC += -DCONFIG_UDP -DCONFIG_UDP_PORT=$(CONFIG_UDP_PORT) 38 | endif 39 | 40 | ifdef CONFIG_PROTECT_STACK 41 | CC += -DCONFIG_PROTECT_STACK 42 | endif 43 | 44 | ifdef CONFIG_LIBCORE 45 | CC += -I$(ROOT)/core/ -DCONFIG_LIBCORE 46 | LIBS += $(ROOT)/core/libcore.a 47 | SRCS += steppers.c 48 | endif 49 | 50 | ifdef CONFIG_LIBMODBUS 51 | SRCS += uart.c 52 | CC += -I$(ROOT)/libmodbus/ -DCONFIG_LIBMODBUS 53 | LIBS += $(ROOT)/libmodbus/libmodbus.a 54 | endif 55 | 56 | ifdef CONFIG_ETHERNET_DEVICE_ENC28J60 57 | CC += -I$(ROOT)/drivers/ethernet_enc28j60/include -DCONFIG_ETHERNET_DEVICE_ENC28J60 58 | LIBS += $(ROOT)/drivers/ethernet_enc28j60/libenc28j60.a 59 | endif 60 | 61 | OBJS := $(SRCS:%.c=%.o) 62 | SUS := $(SRCS:%.c=%.su) 63 | 64 | PWD := $(shell pwd) 65 | 66 | CC += -I$(PWD) -I$(PWD)/libopencm3/include 67 | 68 | LDFLAGS := -T $(PWD)/stm32.ld -Wl,--gc-sections --static -nostartfiles -specs=nano.specs -specs=nosys.specs 69 | 70 | all : controller.elf controller.bin 71 | 72 | %.o : %.c 73 | $(CC) -c $< -o $@ 74 | 75 | controller.bin: controller.elf 76 | $(OBJCOPY) -O binary $< $@ 77 | 78 | controller.elf: libopencm3 $(OBJS) 79 | $(LD) $(LDFLAGS) $(OBJS) libopencm3/lib/libopencm3_stm32f3.a $(LIBS) -o $@ -lm 80 | 81 | flash: controller.bin 82 | openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg -c "init; reset halt; flash write_image erase $< 0x08000000 bin; reset; exit" 83 | 84 | libopencm3: 85 | $(MAKE) -C $(mkfile_dir)/libopencm3/ 86 | 87 | clean: 88 | rm -f $(OBJS) controller.bin controller.elf $(SUS) 89 | -------------------------------------------------------------------------------- /arch/stm32f302/arch-defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | -------------------------------------------------------------------------------- /arch/stm32f302/clock-arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef int clock_time_t; 4 | #define CLOCK_CONF_SECOND 1000 -------------------------------------------------------------------------------- /arch/stm32f302/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DEBUG 0 4 | #define SHELL_BAUDRATE 38400 5 | #define LOCK_ON_CRC_ERROR 0 6 | 7 | -------------------------------------------------------------------------------- /arch/stm32f302/flash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "init; reset halt; flash write_image erase $1 0x08000000 bin; reset; exit" 4 | -------------------------------------------------------------------------------- /arch/stm32f302/gdb-launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GDB=gdb-multiarch 4 | 5 | $GDB "$1" -ex 'target remote localhost:3333' 6 | -------------------------------------------------------------------------------- /arch/stm32f302/iface.c: -------------------------------------------------------------------------------- 1 | #define STM32F3 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "iface.h" 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #define SPI_ENABLE_DMA false 23 | #define SPI_DMA_MINIMAL 16 24 | 25 | /* SPI */ 26 | 27 | #define SPI_RCC RCC_SPI2 28 | #define SPI_ID SPI2 29 | #define SPI_PORT GPIOB 30 | #define SPI_NSS GPIO12 31 | #define SPI_SCK GPIO13 32 | #define SPI_MISO GPIO14 33 | #define SPI_MOSI GPIO15 34 | 35 | /* Ethernet */ 36 | 37 | #define ETH_RST_PORT GPIOB 38 | #define ETH_RST_PIN GPIO11 39 | 40 | uint8_t spi_rw(uint8_t data) 41 | { 42 | spi_send8(SPI_ID, data); 43 | uint8_t c = spi_read8(SPI_ID); 44 | return c; 45 | } 46 | 47 | void spi_cs(bool enable) 48 | { 49 | if (enable) 50 | { 51 | //spi_set_nss_low(SPI_ID); 52 | gpio_clear(SPI_PORT, SPI_NSS); 53 | } 54 | else 55 | { 56 | //spi_set_nss_high(SPI_ID); 57 | gpio_set(SPI_PORT, SPI_NSS); 58 | } 59 | } 60 | 61 | void spi_write_buf(const uint8_t *data, size_t len) 62 | { 63 | while (len-- > 0) 64 | { 65 | spi_rw(*(data++)); 66 | } 67 | } 68 | 69 | void spi_read_buf(uint8_t *data, size_t len) 70 | { 71 | while (len-- > 0) 72 | { 73 | *(data++) = spi_rw(0xFF); 74 | } 75 | } 76 | 77 | void spi_setup(void) 78 | { 79 | int i; 80 | /* Enable SPI2 */ 81 | rcc_periph_clock_enable(SPI_RCC); 82 | 83 | for (i = 0; i < 100000; i++) 84 | asm("nop"); 85 | 86 | gpio_mode_setup(SPI_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, SPI_MOSI | SPI_SCK | SPI_MISO); 87 | gpio_set_af(SPI_PORT, GPIO_AF5, SPI_SCK | SPI_MISO | SPI_MOSI); 88 | gpio_set_output_options(SPI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, SPI_SCK | SPI_MOSI); 89 | 90 | gpio_mode_setup(SPI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, SPI_NSS); 91 | gpio_set_output_options(SPI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, SPI_NSS); 92 | gpio_set(SPI_PORT, SPI_NSS); 93 | 94 | spi_set_master_mode(SPI_ID); 95 | spi_set_baudrate_prescaler(SPI_ID, SPI_CR1_BR_FPCLK_DIV_64); 96 | spi_set_clock_polarity_0(SPI_ID); 97 | spi_set_clock_phase_0(SPI_ID); 98 | spi_set_full_duplex_mode(SPI_ID); 99 | spi_set_unidirectional_mode(SPI_ID); /* bidirectional but in 3-wire */ 100 | spi_set_data_size(SPI_ID, SPI_CR2_DS_8BIT); 101 | spi_enable_software_slave_management(SPI_ID); 102 | spi_send_msb_first(SPI_ID); 103 | spi_set_nss_high(SPI_ID); 104 | //spi_enable_ss_output(SPI_ID); 105 | spi_fifo_reception_threshold_8bit(SPI_ID); 106 | SPI_I2SCFGR(SPI_ID) &= ~SPI_I2SCFGR_I2SMOD; 107 | spi_enable(SPI_ID); 108 | } 109 | 110 | /* Network */ 111 | static xSemaphoreHandle ethMutex = NULL; 112 | static struct enc28j60_state_s eth_state; 113 | static uint8_t eth_mac[6]; 114 | 115 | static void eth_hard_reset(bool rst) 116 | { 117 | if (rst) 118 | gpio_clear(ETH_RST_PORT, ETH_RST_PIN); 119 | else 120 | gpio_set(ETH_RST_PORT, ETH_RST_PIN); 121 | } 122 | 123 | void send_ethernet_frame(const uint8_t *payload, size_t payload_len) 124 | { 125 | if (!xSemaphoreTake(ethMutex, portMAX_DELAY)) 126 | return; 127 | enc28j60_send_data(ð_state, payload, payload_len); 128 | xSemaphoreGive(ethMutex); 129 | } 130 | 131 | static uint8_t eth_buf[1524]; 132 | static void netTask(void *arg) 133 | { 134 | while (true) 135 | { 136 | if (!xSemaphoreTake(ethMutex, portMAX_DELAY)) 137 | continue; 138 | bool received = enc28j60_has_package(ð_state); 139 | xSemaphoreGive(ethMutex); 140 | 141 | if (received) 142 | { 143 | uint32_t status, crc; 144 | 145 | if (!xSemaphoreTake(ethMutex, portMAX_DELAY)) 146 | continue; 147 | ssize_t len = enc28j60_read_packet(ð_state, eth_buf, sizeof(eth_buf), &status, &crc); 148 | xSemaphoreGive(ethMutex); 149 | 150 | if (len > 0) 151 | { 152 | libip_handle_ethernet(eth_buf, len); 153 | } 154 | } 155 | } 156 | } 157 | 158 | void ifaceInitialise(const uint8_t mac[6]) 159 | { 160 | memcpy(eth_mac, mac, 6); 161 | /* Init SPI2 */ 162 | spi_setup(); 163 | 164 | /* Configure hard reset pin */ 165 | gpio_mode_setup(ETH_RST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, ETH_RST_PIN); 166 | gpio_set_output_options(ETH_RST_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, ETH_RST_PIN); 167 | gpio_set(ETH_RST_PORT, ETH_RST_PIN); 168 | 169 | /* Init enc28j60 module */ 170 | taskENTER_CRITICAL(); 171 | enc28j60_init(ð_state, eth_hard_reset, spi_rw, spi_cs, spi_write_buf, spi_read_buf); 172 | bool res = enc28j60_configure(ð_state, eth_mac, 4096, true); 173 | taskEXIT_CRITICAL(); 174 | 175 | ethMutex = xSemaphoreCreateMutex(); 176 | } 177 | 178 | void ifaceStart(void) 179 | { 180 | /* Create task for enc28j60 polling and sending data */ 181 | xTaskCreate(netTask, "enc28j60", 2048, NULL, 0, NULL); 182 | } 183 | -------------------------------------------------------------------------------- /arch/stm32f302/iface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | uint8_t spi_rw(uint8_t data); 8 | void spi_cs(bool enable); 9 | void spi_write_buf(const uint8_t *data, size_t len); 10 | void spi_read_buf(uint8_t *data, size_t len); 11 | 12 | void spi_setup(void); 13 | 14 | void ifaceInitialise(const uint8_t mac[6]); 15 | void ifaceStart(void); 16 | -------------------------------------------------------------------------------- /arch/stm32f302/libopencm3: -------------------------------------------------------------------------------- 1 | ../stm32f103/libopencm3/ -------------------------------------------------------------------------------- /arch/stm32f302/main.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifdef CONFIG_LIBCORE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "steppers.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "platform.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | static xSemaphoreHandle shellMutex = NULL; 26 | 27 | #ifdef CONFIG_LIBCORE 28 | static void init_steppers(void) 29 | { 30 | gpio_definition gd; 31 | 32 | steppers_definition sd = {}; 33 | steppers_config(&sd, &gd); 34 | init_control(&sd, &gd); 35 | } 36 | 37 | static void preCalculateTask(void *args) 38 | { 39 | while (true) 40 | { 41 | xSemaphoreTake(shellMutex, portMAX_DELAY); 42 | planner_report_states(); 43 | xSemaphoreGive(shellMutex); 44 | 45 | planner_pre_calculate(); 46 | } 47 | } 48 | #endif 49 | 50 | static void heartBeatTask(void *args) 51 | { 52 | while (true) 53 | { 54 | vTaskDelay(500 / portTICK_PERIOD_MS); 55 | led_off(); 56 | vTaskDelay(200 / portTICK_PERIOD_MS); 57 | led_on(); 58 | vTaskDelay(100 / portTICK_PERIOD_MS); 59 | led_off(); 60 | vTaskDelay(200 / portTICK_PERIOD_MS); 61 | led_on(); 62 | } 63 | } 64 | 65 | static void net_setup(void) 66 | { 67 | const char *ipstr = CONFIG_IP_IPADDR; 68 | const char *macstr = CONFIG_ETHERNET_MAC_ADDR; 69 | 70 | unsigned mac[6]; 71 | unsigned ip[4]; 72 | sscanf(ipstr, "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]); 73 | sscanf(macstr, "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); 74 | 75 | uint32_t ipaddr = (ip[0] << 24) | (ip[1] << 16) | (ip[2] << 8) | (ip[3]); 76 | uint8_t macaddr[6] = {(uint8_t)(mac[0]), (uint8_t)(mac[1]), (uint8_t)(mac[2]), (uint8_t)(mac[3]), (uint8_t)(mac[4]), (uint8_t)(mac[5])}; 77 | ifaceInitialise(macaddr); 78 | libip_init(ipaddr, macaddr); 79 | shell_setup(NULL, NULL/*, create_mutex, take_mutex, release_mutex*/); 80 | ifaceStart(); 81 | } 82 | 83 | enum rudp_state_e 84 | { 85 | CLOSED = 0, 86 | OPENED, 87 | }; 88 | 89 | static struct 90 | { 91 | enum rudp_state_e state; 92 | uint32_t remote_ip; 93 | uint16_t remote_port; 94 | } connection; 95 | 96 | void udp_packet_handler(uint32_t remote_ip, uint16_t dport, uint16_t sport, const uint8_t *payload, size_t len) 97 | { 98 | if (dport == CONFIG_UDP_PORT) 99 | { 100 | connection.state = OPENED; 101 | connection.remote_ip = remote_ip; 102 | connection.remote_port = sport; 103 | 104 | xSemaphoreTake(shellMutex, portMAX_DELAY); 105 | shell_data_received(payload, len); 106 | shell_data_completed(); 107 | xSemaphoreGive(shellMutex); 108 | } 109 | } 110 | 111 | void shellSendTask(void *args) 112 | { 113 | while (true) 114 | { 115 | xSemaphoreTake(shellMutex, portMAX_DELAY); 116 | do 117 | { 118 | ssize_t len; 119 | const uint8_t *data = shell_pick_message(&len); 120 | if (data == NULL) 121 | { 122 | break; 123 | } 124 | libip_send_udp_packet(data, len, connection.remote_ip, CONFIG_UDP_PORT, connection.remote_port); 125 | shell_send_completed(); 126 | } while(0); 127 | xSemaphoreGive(shellMutex); 128 | } 129 | } 130 | 131 | int main(void) 132 | { 133 | hardware_setup(); 134 | 135 | #ifdef CONFIG_LIBCORE 136 | init_steppers(); 137 | planner_lock(); 138 | moves_reset(); 139 | #endif 140 | 141 | net_setup(); 142 | 143 | #ifdef CONFIG_LIBCORE 144 | xTaskCreate(preCalculateTask, "precalc", configMINIMAL_STACK_SIZE, NULL, 0, NULL); 145 | #endif 146 | 147 | shellMutex = xSemaphoreCreateMutex(); 148 | 149 | xTaskCreate(shellSendTask, "shell", configMINIMAL_STACK_SIZE, NULL, 0, NULL); 150 | xTaskCreate(heartBeatTask, "hb", configMINIMAL_STACK_SIZE, NULL, 0, NULL); 151 | vTaskStartScheduler(); 152 | 153 | while (true) 154 | ; 155 | 156 | return 0; 157 | } 158 | 159 | /*** ***********************/ 160 | 161 | void vApplicationTickHook(void) 162 | { 163 | } 164 | 165 | void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) 166 | { 167 | for (;;) 168 | ; 169 | } 170 | 171 | void vApplicationMallocFailedHook(void) 172 | { 173 | for (;;) 174 | ; 175 | } 176 | 177 | void vApplicationIdleHook(void) 178 | { 179 | } 180 | 181 | /*** ***********************/ 182 | -------------------------------------------------------------------------------- /arch/stm32f302/ocd-launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg 4 | -------------------------------------------------------------------------------- /arch/stm32f302/platform.c: -------------------------------------------------------------------------------- 1 | #define STM32F3 2 | 3 | #include "platform.h" 4 | #include "iface.h" 5 | 6 | #include 7 | 8 | #ifdef CONFIG_SPI 9 | #include "spi.h" 10 | #endif 11 | 12 | #ifdef CONFIG_LIBCORE 13 | #include "steppers.h" 14 | #endif 15 | 16 | #ifdef CONFIG_UART 17 | #include "uart.h" 18 | #endif 19 | 20 | #ifdef CONFIG_ETHERNET_DEVICE_ENC28J60 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* RCC */ 30 | static void clock_setup(void) 31 | { 32 | rcc_clock_setup_pll(&rcc_hse8mhz_configs[RCC_CLOCK_HSE8_72MHZ]); 33 | 34 | /* Enable GPIOA clock. */ 35 | rcc_periph_clock_enable(RCC_GPIOA); 36 | rcc_periph_clock_enable(RCC_GPIOB); 37 | rcc_periph_clock_enable(RCC_GPIOC); 38 | 39 | /* Enable clocks for GPIO port A (for GPIO_USART1_TX) and USART1. */ 40 | rcc_periph_clock_enable(RCC_USART1); 41 | 42 | /* Enable TIM2 clock for steps*/ 43 | rcc_periph_clock_enable(RCC_TIM2); 44 | 45 | /* Enable TIM3 clock for connection timeouts */ 46 | rcc_periph_clock_enable(RCC_TIM3); 47 | 48 | // Delay 49 | int i; 50 | for (i = 0; i < 100000; i++) 51 | __asm__("nop"); 52 | } 53 | 54 | static void gpio_setup(void) 55 | { 56 | /* Blink led */ 57 | gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_OTYPE_PP, GPIO13); 58 | } 59 | 60 | static void eth_hard_reset(bool rst) 61 | { 62 | if (rst) 63 | gpio_clear(GPIOB, GPIO11); 64 | else 65 | gpio_set(GPIOB, GPIO11); 66 | } 67 | 68 | void hardware_setup(void) 69 | { 70 | clock_setup(); 71 | gpio_setup(); 72 | 73 | #ifdef CONFIG_UART 74 | uart_setup(9600); 75 | #endif 76 | 77 | #ifdef CONFIG_LIBCORE 78 | steppers_setup(); 79 | #endif 80 | 81 | gpio_set(GPIOC, GPIO13); 82 | 83 | int i; 84 | for (i = 0; i < 0x400000; i++) 85 | __asm__("nop"); 86 | } 87 | 88 | #ifdef CONFIG_PROTECT_STACK 89 | void __wrap___stack_chk_fail(void) 90 | { 91 | static int i; 92 | while (1) 93 | { 94 | for (i = 0; i < 0x40000; i++) 95 | __asm__("nop"); 96 | gpio_set(GPIOC, GPIO13); 97 | for (i = 0; i < 0x40000; i++) 98 | __asm__("nop"); 99 | gpio_clear(GPIOC, GPIO13); 100 | } 101 | } 102 | #endif 103 | 104 | void led_on(void) 105 | { 106 | gpio_set(GPIOC, GPIO13); 107 | } 108 | 109 | void led_off(void) 110 | { 111 | gpio_clear(GPIOC, GPIO13); 112 | } 113 | 114 | struct fault_state_s 115 | { 116 | uint32_t r0; 117 | uint32_t r1; 118 | uint32_t r2; 119 | uint32_t r3; 120 | uint32_t r12; 121 | uint32_t lr; 122 | uint32_t pc; 123 | uint32_t psr; 124 | }; 125 | 126 | void hard_fault_handler(void) 127 | { 128 | struct fault_state_s *stack_ptr; 129 | 130 | asm( 131 | "TST lr, #4 \n" //Тестируем 3ий бит указателя стека(побитовое И) 132 | "ITE EQ \n" //Значение указателя стека имеет бит 3? 133 | "MRSEQ %[ptr], MSP \n" //Да, сохраняем основной указатель стека 134 | "MRSNE %[ptr], PSP \n" //Нет, сохраняем указатель стека процесса 135 | : [ptr] "=r" (stack_ptr) 136 | ); 137 | 138 | uint8_t *prev_stack = (uint8_t *)stack_ptr - sizeof(struct fault_state_s); 139 | 140 | while (1) 141 | { 142 | static int i; 143 | for (i = 0; i < 0x400000; i++) 144 | __asm__("nop"); 145 | gpio_set(GPIOC, GPIO13); 146 | for (i = 0; i < 0x400000; i++) 147 | __asm__("nop"); 148 | gpio_clear(GPIOC, GPIO13); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /arch/stm32f302/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void hardware_setup(void); 4 | 5 | void network_loop(void); 6 | 7 | void hard_fault_handler(void); 8 | 9 | void led_on(void); 10 | void led_off(void); 11 | 12 | -------------------------------------------------------------------------------- /arch/stm32f302/steppers.c: -------------------------------------------------------------------------------- 1 | #define STM32F3 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "config.h" 13 | #include "steppers.h" 14 | 15 | #define FCPU 72000000UL 16 | #define FTIMER 100000UL 17 | #define PSC ((FCPU) / (FTIMER) - 1) 18 | #define TIMEOUT_TIMER_STEP 1000UL 19 | 20 | static bool going = false; 21 | 22 | static void steppers_timer_setup(void) 23 | { 24 | rcc_periph_reset_pulse(RST_TIM2); 25 | 26 | timer_set_prescaler(TIM2, PSC); 27 | timer_direction_up(TIM2); 28 | timer_disable_preload(TIM2); 29 | timer_update_on_overflow(TIM2); 30 | timer_enable_update_event(TIM2); 31 | timer_continuous_mode(TIM2); 32 | 33 | timer_set_oc_fast_mode(TIM2, TIM_OC1); 34 | timer_set_oc_value(TIM2, TIM_OC1, 1); 35 | 36 | nvic_set_priority(NVIC_TIM2_IRQ, 0x00); 37 | 38 | nvic_enable_irq(NVIC_TIM2_IRQ); 39 | timer_enable_irq(TIM2, TIM_DIER_UIE); 40 | timer_enable_irq(TIM2, TIM_DIER_CC1IE); 41 | 42 | nvic_set_priority(NVIC_TIM2_IRQ, 6 * 16); 43 | } 44 | 45 | void steppers_timer_irq_enable(bool en) 46 | { 47 | if (en) 48 | { 49 | nvic_enable_irq(NVIC_TIM2_IRQ); 50 | } 51 | else 52 | { 53 | nvic_disable_irq(NVIC_TIM2_IRQ); 54 | } 55 | } 56 | 57 | void steppers_setup(void) 58 | { 59 | steppers_timer_setup(); 60 | 61 | // X - step 62 | gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO14); 63 | gpio_set_output_options(GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO14); 64 | // X - dir 65 | gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO15); 66 | gpio_set_output_options(GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO15); 67 | 68 | // Y - step 69 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO0); 70 | gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO0); 71 | // Y - dir 72 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO1); 73 | gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO1); 74 | 75 | // Z - step 76 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO2); 77 | gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO2); 78 | // Z - dir 79 | gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO3); 80 | gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO3); 81 | 82 | // Enable 83 | gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO0); 84 | gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO0); 85 | 86 | // X - stop 87 | gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO7); 88 | 89 | // Y - stop 90 | gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO6); 91 | 92 | // Z - stop 93 | gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO5); 94 | 95 | // Probe 96 | gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO8); 97 | 98 | // GPIO Tool 0 99 | gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO1); 100 | gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO1); 101 | } 102 | 103 | static volatile int moving; 104 | 105 | static void set_dir(int i, bool dir) 106 | { 107 | if (dir) { 108 | switch (i) { 109 | case 0: 110 | gpio_set(GPIOC, GPIO15); 111 | break; 112 | case 1: 113 | gpio_set(GPIOA, GPIO1); 114 | break; 115 | case 2: 116 | gpio_set(GPIOA, GPIO3); 117 | break; 118 | } 119 | } else { 120 | switch (i) { 121 | case 0: 122 | gpio_clear(GPIOC, GPIO15); 123 | break; 124 | case 1: 125 | gpio_clear(GPIOA, GPIO1); 126 | break; 127 | case 2: 128 | gpio_clear(GPIOA, GPIO3); 129 | break; 130 | } 131 | } 132 | } 133 | 134 | static void make_step(int i) 135 | { 136 | switch (i) 137 | { 138 | case 0: 139 | gpio_clear(GPIOC, GPIO14); 140 | break; 141 | case 1: 142 | gpio_clear(GPIOA, GPIO0); 143 | break; 144 | case 2: 145 | gpio_clear(GPIOA, GPIO2); 146 | break; 147 | } 148 | } 149 | 150 | static void end_step(void) 151 | { 152 | gpio_set(GPIOC, GPIO14); 153 | gpio_set(GPIOA, GPIO0); 154 | gpio_set(GPIOA, GPIO2); 155 | } 156 | 157 | static void make_tick(void) 158 | { 159 | int delay_us = moves_step_tick(); 160 | if (delay_us < 0) 161 | { 162 | return; 163 | } 164 | int delay = delay_us * FTIMER / 1000000UL; 165 | if (delay < 3) 166 | delay = 3; 167 | timer_set_counter(TIM2, 0); 168 | timer_set_period(TIM2, delay); 169 | } 170 | 171 | void tim2_isr(void) 172 | { 173 | if (!going) 174 | { 175 | TIM_SR(TIM2) &= ~TIM_SR_UIF; 176 | TIM_SR(TIM2) &= ~TIM_SR_CC1IF; 177 | 178 | timer_disable_counter(TIM2); 179 | return; 180 | } 181 | if (TIM_SR(TIM2) & TIM_SR_UIF) { 182 | // next step of movement 183 | // it can set STEP pins active (low) 184 | TIM_SR(TIM2) &= ~TIM_SR_UIF; 185 | make_tick(); 186 | } 187 | else if (TIM_SR(TIM2) & TIM_SR_CC1IF) { 188 | // set STEP pins not active (low) at the end of STEP 189 | // ___ 190 | // ______| |_______ 191 | // 192 | // ^ 193 | // | 194 | // here 195 | TIM_SR(TIM2) &= ~TIM_SR_CC1IF; 196 | end_step(); 197 | } 198 | } 199 | 200 | static void line_started(void) 201 | { 202 | // PC13 has LED. Enable it 203 | gpio_clear(GPIOC, GPIO13); 204 | 205 | // Set initial STEP state 206 | end_step(); 207 | moving = 1; 208 | 209 | // first tick 210 | going = true; 211 | make_tick(); 212 | if (going) 213 | timer_enable_counter(TIM2); 214 | } 215 | 216 | static void line_finished(void) 217 | { 218 | timer_disable_counter(TIM2); 219 | going = false; 220 | 221 | // disable LED 222 | gpio_set(GPIOC, GPIO13); 223 | 224 | // initial STEP state 225 | end_step(); 226 | moving = 0; 227 | } 228 | 229 | static void line_error(void) 230 | { 231 | // temporary do same things as in finished case 232 | timer_disable_counter(TIM2); 233 | going = false; 234 | 235 | gpio_set(GPIOC, GPIO13); 236 | end_step(); 237 | moving = 0; 238 | } 239 | 240 | static void set_gpio(int id, int on) 241 | { 242 | switch (id) 243 | { 244 | case 0: 245 | if (on) 246 | gpio_clear(GPIOB, GPIO1); 247 | else 248 | gpio_set(GPIOB, GPIO1); 249 | break; 250 | } 251 | } 252 | 253 | static cnc_endstops get_stops(void) 254 | { 255 | cnc_endstops stops = { 256 | .stop_x = !(gpio_get(GPIOB, GPIO7)), 257 | .stop_y = !(gpio_get(GPIOB, GPIO6)), 258 | .stop_z = !(gpio_get(GPIOB, GPIO5)), 259 | .probe = !(gpio_get(GPIOB, GPIO8)), 260 | }; 261 | 262 | return stops; 263 | } 264 | 265 | static void reboot(void) 266 | { 267 | SCB_AIRCR = (0x5FA<<16)|(1 << 2); 268 | for (;;) 269 | ; 270 | } 271 | 272 | void steppers_config(steppers_definition *sd, gpio_definition *gd) 273 | { 274 | sd->reboot = reboot; 275 | sd->set_dir = set_dir; 276 | sd->make_step = make_step; 277 | sd->get_endstops = get_stops; 278 | sd->line_started = line_started; 279 | sd->line_finished = line_finished; 280 | sd->line_error = line_error; 281 | gd->set_gpio = set_gpio; 282 | } 283 | 284 | -------------------------------------------------------------------------------- /arch/stm32f302/steppers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void steppers_setup(void); 7 | 8 | void steppers_config(steppers_definition *sd, gpio_definition *gd); 9 | 10 | -------------------------------------------------------------------------------- /arch/stm32f302/stm32.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K 4 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 32K 5 | } 6 | 7 | INCLUDE libopencm3/lib/cortex-m-generic.ld 8 | -------------------------------------------------------------------------------- /arch/stm32f302/uart.c: -------------------------------------------------------------------------------- 1 | #define STM32F3 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "config.h" 12 | 13 | #define UART_ID USART1 14 | #define UART_PORT GPIOA 15 | #define UART_TX GPIO9 16 | #define UART_RX GPIO10 17 | #define UART_RTS GPIO12 18 | 19 | static volatile int i; 20 | void uart_send(const uint8_t *data, size_t len) 21 | { 22 | gpio_clear(UART_PORT, UART_RTS); 23 | for (i = 0; i < 100000; i++) 24 | __asm__("nop"); 25 | while (len > 0) 26 | { 27 | uint16_t d = *(data++); 28 | while ((USART_ISR(UART_ID) & USART_ISR_TXE) == 0) 29 | ; 30 | USART_TDR(UART_ID) = d; 31 | // usart_send_blocking(USART1, d); 32 | len--; 33 | } 34 | for (i = 0; i < 100000; i++) 35 | __asm__("nop"); 36 | gpio_set(UART_PORT, UART_RTS); 37 | } 38 | 39 | void uart_setup(int baudrate) 40 | { 41 | // nvic_enable_irq(NVIC_USART1_IRQ); 42 | 43 | /* Setup GPIO pin GPIO_USART1_RE_RX on GPIO port A for receive. */ 44 | gpio_mode_setup(UART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, UART_RX | UART_TX); 45 | gpio_set_af(UART_PORT, GPIO_AF7, UART_RX | UART_TX); 46 | 47 | /* Setup GPIO pin GPIO_USART1_RTS. */ 48 | gpio_mode_setup(UART_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, UART_RTS); 49 | gpio_set_output_options(UART_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, UART_RTS); 50 | gpio_set(UART_PORT, UART_RTS); 51 | 52 | /* Setup UART parameters. */ 53 | usart_set_baudrate(UART_ID, baudrate); 54 | usart_set_databits(UART_ID, 8); 55 | usart_set_stopbits(UART_ID, USART_STOPBITS_1); 56 | usart_set_mode(UART_ID, USART_MODE_TX_RX); 57 | usart_set_parity(UART_ID, USART_PARITY_NONE); 58 | usart_set_flow_control(UART_ID, USART_FLOWCONTROL_NONE); 59 | 60 | USART_CR1(UART_ID) |= USART_CR1_RXNEIE; 61 | USART_CR1(UART_ID) |= USART_CR1_TCIE; 62 | 63 | /* Finally enable the USART. */ 64 | usart_enable(UART_ID); 65 | } 66 | -------------------------------------------------------------------------------- /arch/stm32f302/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void uart_setup(int baudrate); 8 | 9 | void uart_send(const uint8_t *buf, size_t len); 10 | 11 | -------------------------------------------------------------------------------- /arch/stm32f302/uip-conf.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \addtogroup uipopt 3 | * @{ 4 | */ 5 | 6 | /** 7 | * \name Project-specific configuration options 8 | * @{ 9 | * 10 | * uIP has a number of configuration options that can be overridden 11 | * for each project. These are kept in a project-specific uip-conf.h 12 | * file and all configuration names have the prefix UIP_CONF. 13 | */ 14 | 15 | /* 16 | * Copyright (c) 2006, Swedish Institute of Computer Science. 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions 21 | * are met: 22 | * 1. Redistributions of source code must retain the above copyright 23 | * notice, this list of conditions and the following disclaimer. 24 | * 2. Redistributions in binary form must reproduce the above copyright 25 | * notice, this list of conditions and the following disclaimer in the 26 | * documentation and/or other materials provided with the distribution. 27 | * 3. Neither the name of the Institute nor the names of its contributors 28 | * may be used to endorse or promote products derived from this software 29 | * without specific prior written permission. 30 | * 31 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 32 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 35 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 39 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 40 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41 | * SUCH DAMAGE. 42 | * 43 | * This file is part of the uIP TCP/IP stack 44 | * 45 | * $Id: uip-conf.h,v 1.6 2006/06/12 08:00:31 adam Exp $ 46 | */ 47 | 48 | /** 49 | * \file 50 | * An example uIP configuration file 51 | * \author 52 | * Adam Dunkels 53 | */ 54 | 55 | #ifndef __UIP_CONF_H__ 56 | #define __UIP_CONF_H__ 57 | 58 | #include 59 | 60 | /** 61 | * 8 bit datatype 62 | * 63 | * This typedef defines the 8-bit type used throughout uIP. 64 | * 65 | * \hideinitializer 66 | */ 67 | typedef uint8_t u8_t; 68 | 69 | /** 70 | * 16 bit datatype 71 | * 72 | * This typedef defines the 16-bit type used throughout uIP. 73 | * 74 | * \hideinitializer 75 | */ 76 | typedef uint16_t u16_t; 77 | 78 | /** 79 | * Statistics datatype 80 | * 81 | * This typedef defines the dataype used for keeping statistics in 82 | * uIP. 83 | * 84 | * \hideinitializer 85 | */ 86 | typedef unsigned short uip_stats_t; 87 | 88 | /** 89 | * Maximum number of TCP connections. 90 | * 91 | * \hideinitializer 92 | */ 93 | #define UIP_CONF_MAX_CONNECTIONS 1 94 | 95 | /** 96 | * Maximum number of listening TCP ports. 97 | * 98 | * \hideinitializer 99 | */ 100 | #define UIP_CONF_MAX_LISTENPORTS 1 101 | 102 | /** 103 | * uIP buffer size. 104 | * 105 | * \hideinitializer 106 | */ 107 | #define UIP_CONF_BUFFER_SIZE 1600 108 | 109 | /** 110 | * CPU byte order. 111 | * 112 | * \hideinitializer 113 | */ 114 | #define UIP_CONF_BYTE_ORDER LITTLE_ENDIAN 115 | 116 | /** 117 | * Logging on or off 118 | * 119 | * \hideinitializer 120 | */ 121 | #define UIP_CONF_LOGGING 0 122 | 123 | /** 124 | * UDP support on or off 125 | * 126 | * \hideinitializer 127 | */ 128 | #define UIP_CONF_UDP 0 129 | 130 | /** 131 | * UDP checksums on or off 132 | * 133 | * \hideinitializer 134 | */ 135 | #define UIP_CONF_UDP_CHECKSUMS 1 136 | 137 | /** 138 | * uIP statistics on or off 139 | * 140 | * \hideinitializer 141 | */ 142 | #define UIP_CONF_STATISTICS 1 143 | 144 | /* Here we include the header file for the application(s) we use in 145 | our project. */ 146 | 147 | #include "server.h" 148 | 149 | 150 | #endif /* __UIP_CONF_H__ */ 151 | 152 | /** @} */ 153 | /** @} */ 154 | -------------------------------------------------------------------------------- /core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(core INTERFACE) 3 | target_include_directories(core INTERFACE .) 4 | 5 | add_subdirectory(err/) 6 | add_subdirectory(control/) 7 | add_subdirectory(gcode/) 8 | add_subdirectory(output/) 9 | 10 | #add_subdirectory(unit_tests) 11 | 12 | -------------------------------------------------------------------------------- /core/Makefile: -------------------------------------------------------------------------------- 1 | SRCS := ./output/output.c \ 2 | ./gcode/gcodes.c \ 3 | ./control/system.c \ 4 | ./control/control.c \ 5 | ./control/moves/moves.c \ 6 | ./control/moves/moves_common/common.c \ 7 | ./control/moves/moves_common/acceleration.c \ 8 | ./control/moves/moves_line/line.c \ 9 | ./control/ioqueue/print_events.c \ 10 | ./control/commands/gcode_handler/gcode_handler.c \ 11 | ./control/commands/status/print_status.c \ 12 | ./control/planner/planner.c \ 13 | ./control/tools/tools.c \ 14 | ./control/moves/moves_arc/arc.c 15 | 16 | HEADERS := ./control/system.h \ 17 | ./control/moves/moves_common/common.h \ 18 | ./control/moves/moves_common/steppers.h \ 19 | ./control/moves/moves_common/acceleration.h \ 20 | ./control/moves/moves_arc/arc.h \ 21 | ./control/moves/moves.h \ 22 | ./control/moves/moves_line/line.h \ 23 | ./control/ioqueue/print_events.h \ 24 | ./control/control.h \ 25 | ./control/commands/gcode_handler/gcode_handler.h \ 26 | ./control/commands/status/print_status.h \ 27 | ./control/planner/planner.h \ 28 | ./control/tools/tools.h \ 29 | ./defs.h \ 30 | ./err/err.h \ 31 | ./gcode/gcodes.h \ 32 | ./output/output.h 33 | 34 | OBJS := $(SRCS:%.c=%.o) 35 | SUS := $(SRCS:%.c=%.su) 36 | TARGET := libcore.a 37 | 38 | 39 | TESTS_SRCS := ./unit_tests/test_lines.c \ 40 | ./unit_tests/test_gcode.c \ 41 | ./unit_tests/test_planner.c \ 42 | ./control/moves/unit/test_line.c \ 43 | ./control/moves/unit/test_arc.c 44 | 45 | ifdef CONFIG_QUEUE_SIZE 46 | CC += -DQUEUE_SIZE=${CONFIG_QUEUE_SIZE} 47 | endif 48 | 49 | CC += -I./ 50 | 51 | all: $(TARGET) 52 | 53 | %.o : %.c $(HEADERS) Makefile 54 | $(CC) -c $< -o $@ 55 | 56 | $(TARGET): $(OBJS) 57 | $(AR) rsc $@ $^ 58 | 59 | clean: 60 | rm -f $(OBJS) $(TARGET) $(SUS) 61 | 62 | -------------------------------------------------------------------------------- /core/control/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(system STATIC system.c) 2 | target_include_directories(system PUBLIC .) 3 | 4 | add_library(control STATIC control.c) 5 | target_include_directories(control PUBLIC .) 6 | target_link_libraries(control moves planner ioqueue gcode_handler status system) 7 | 8 | add_subdirectory(moves/) 9 | add_subdirectory(tools/) 10 | add_subdirectory(planner/) 11 | add_subdirectory(ioqueue/) 12 | add_subdirectory(commands/) 13 | -------------------------------------------------------------------------------- /core/control/commands/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(status/) 2 | add_subdirectory(gcode_handler/) 3 | -------------------------------------------------------------------------------- /core/control/commands/gcode_handler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(gcode_handler STATIC gcode_handler.c) 3 | target_include_directories(status PUBLIC .) 4 | target_link_libraries(gcode_handler err gcode ioqueue status planner system) 5 | -------------------------------------------------------------------------------- /core/control/commands/gcode_handler/gcode_handler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static int handle_g_command(gcode_frame_t *frame) 16 | { 17 | gcode_cmd_t *cmds = frame->cmds; 18 | int ncmds = frame->num; 19 | int nid = -1; 20 | 21 | // skip line number(s) 22 | while (ncmds > 0 && cmds[0].type == 'N') { 23 | nid = cmds[0].val_i; 24 | ncmds--; 25 | cmds++; 26 | } 27 | 28 | if (nid == -1) 29 | { 30 | send_error(-1, "No command number specified"); 31 | planner_lock(); 32 | return -E_INCORRECT; 33 | } 34 | if (ncmds == 0) 35 | { 36 | send_ok(nid); 37 | return -E_OK; 38 | } 39 | 40 | // parse command line 41 | switch (cmds[0].type) { 42 | case 'G': 43 | switch (cmds[0].val_i) { 44 | case 0: 45 | case 1: { 46 | int i; 47 | double f = 0, feed0 = 0, feed1 = 0; 48 | double acc = 0; 49 | int32_t x[3] = {0, 0, 0}; 50 | for (i = 1; i < ncmds; i++) { 51 | switch (cmds[i].type) { 52 | case 'X': 53 | x[0] = cmds[i].val_i; 54 | break; 55 | case 'Y': 56 | x[1] = cmds[i].val_i; 57 | break; 58 | case 'Z': 59 | x[2] = cmds[i].val_i; 60 | break; 61 | case 'F': 62 | f = cmds[i].val_f; 63 | break; 64 | case 'P': 65 | feed0 = cmds[i].val_f; 66 | break; 67 | case 'L': 68 | feed1 = cmds[i].val_f; 69 | break; 70 | case 'T': 71 | acc = cmds[i].val_f; 72 | break; 73 | } 74 | } 75 | int res = planner_line_to(x, f, feed0, feed1, acc, nid); 76 | if (res >= 0) 77 | { 78 | return -E_OK; 79 | } 80 | else if (res == -E_NOMEM) 81 | { 82 | send_error(nid, "no space in buffer"); 83 | planner_lock(); 84 | return res; 85 | } 86 | else if (res == -E_LOCKED) 87 | { 88 | send_error(nid, "system is locked"); 89 | return res; 90 | } 91 | else 92 | { 93 | send_error(nid, "problem with planning line"); 94 | planner_lock(); 95 | return res; 96 | } 97 | break; 98 | } 99 | case 2: 100 | case 3: { 101 | int i; 102 | double f = 0, feed0 = 0, feed1 = 0; 103 | int32_t x1[2] = {0, 0}; 104 | int32_t x2[2] = {0, 0}; 105 | int32_t h = 0; 106 | double a = 0, b = 0, len = 0; 107 | 108 | double acc = 0; 109 | int plane = XY; 110 | for (i = 1; i < ncmds; i++) { 111 | switch (cmds[i].type) { 112 | case 'X': 113 | x2[0] = cmds[i].val_i; 114 | break; 115 | case 'Y': 116 | x2[1] = cmds[i].val_i; 117 | break; 118 | case 'R': 119 | x1[0] = cmds[i].val_i; 120 | break; 121 | case 'S': 122 | x1[1] = cmds[i].val_i; 123 | break; 124 | case 'H': 125 | h = cmds[i].val_i; 126 | break; 127 | case 'D': 128 | len = cmds[i].val_f; 129 | break; 130 | case 'A': 131 | a = cmds[i].val_f; 132 | break; 133 | case 'B': 134 | b = cmds[i].val_f; 135 | break; 136 | case 'F': 137 | f = cmds[i].val_f; 138 | break; 139 | case 'P': 140 | feed0 = cmds[i].val_f; 141 | break; 142 | case 'L': 143 | feed1 = cmds[i].val_f; 144 | break; 145 | case 'T': 146 | acc = cmds[i].val_f; 147 | break; 148 | case 'G': 149 | switch (cmds[i].val_i) 150 | { 151 | case 17: 152 | plane = XY; 153 | break; 154 | case 18: 155 | plane = YZ; 156 | break; 157 | case 19: 158 | plane = ZX; 159 | break; 160 | default: 161 | break; 162 | } 163 | break; 164 | } 165 | } 166 | int cw = (cmds[0].val_i == 2); 167 | int res = planner_arc_to(x1, x2, h, len, a, b, plane, cw, f, feed0, feed1, acc, nid); 168 | if (res >= 0) 169 | { 170 | return -E_OK; 171 | } 172 | else if (res == -E_NOMEM) 173 | { 174 | send_error(nid, "no space in buffer"); 175 | planner_lock(); 176 | return res; 177 | } 178 | else if (res == -E_LOCKED) 179 | { 180 | send_error(nid, "system is locked"); 181 | return res; 182 | } 183 | else 184 | { 185 | send_error(nid, "problem with planning arc"); 186 | planner_lock(); 187 | return res; 188 | } 189 | break; 190 | } 191 | default: 192 | { 193 | char buf[60]; 194 | snprintf(buf, 60, "unknown command G%i", cmds[0].val_i); 195 | send_error(nid, buf); 196 | planner_lock(); 197 | return -E_INCORRECT; 198 | } 199 | } 200 | break; 201 | case 'M': 202 | switch (cmds[0].val_i) { 203 | case 3: 204 | case 5: 205 | { 206 | int tool = 0; 207 | int on = (cmds[0].val_i == 3); 208 | int i; 209 | for (i = 1; i < ncmds; i++) { 210 | switch (cmds[i].type) { 211 | case 'D': 212 | tool = cmds[i].val_i; 213 | break; 214 | } 215 | } 216 | int res = planner_tool(tool, on, nid); 217 | if (res >= 0) 218 | { 219 | return -E_OK; 220 | } 221 | else if (res == -E_NOMEM) 222 | { 223 | send_error(nid, "no space in buffer"); 224 | planner_lock(); 225 | return res; 226 | } 227 | else if (res == -E_LOCKED) 228 | { 229 | send_error(nid, "system is locked"); 230 | return res; 231 | } 232 | else 233 | { 234 | send_error(nid, "problem with planning tool"); 235 | planner_lock(); 236 | return res; 237 | } 238 | 239 | return -E_OK; 240 | } 241 | case 80: { 242 | if (moves_common_def.enable_step) 243 | moves_common_def.enable_step(true); 244 | send_ok(nid); 245 | return -E_OK; 246 | } 247 | case 81: { 248 | if (moves_common_def.enable_step) 249 | moves_common_def.enable_step(false); 250 | send_ok(nid); 251 | return -E_OK; 252 | } 253 | case 100: { 254 | int i; 255 | steppers_definition def = moves_common_def; 256 | for (i = 1; i < ncmds; i++) { 257 | switch (cmds[i].type) { 258 | case 'X': 259 | def.steps_per_unit[0] = cmds[i].val_f; 260 | break; 261 | case 'Y': 262 | def.steps_per_unit[1] = cmds[i].val_f; 263 | break; 264 | case 'Z': 265 | def.steps_per_unit[2] = cmds[i].val_f; 266 | break; 267 | case 'F': 268 | def.feed_max = cmds[i].val_f; 269 | break; 270 | case 'A': 271 | def.acc_default = cmds[i].val_f; 272 | break; 273 | case 'B': 274 | def.feed_base = cmds[i].val_f; 275 | break; 276 | } 277 | } 278 | 279 | if (def.steps_per_unit[0] > 0 && 280 | def.steps_per_unit[1] > 0 && 281 | def.steps_per_unit[2] > 0 && 282 | def.feed_max > 0 && 283 | def.feed_base > 0 && 284 | def.acc_default > 0) 285 | { 286 | def.configured = true; 287 | } 288 | moves_common_init(&def); 289 | send_ok(nid); 290 | return -E_OK; 291 | } 292 | case 114: 293 | send_queued(nid); 294 | print_position(nid); 295 | return -E_OK; 296 | case 119: 297 | send_queued(nid); 298 | print_endstops(nid); 299 | return -E_OK; 300 | case 800: 301 | planner_unlock(); 302 | send_ok(nid); 303 | return -E_OK; 304 | case 801: 305 | planner_lock(); 306 | send_ok(nid); 307 | return -E_OK; 308 | case 802: 309 | planner_fail_on_endstops(false); 310 | send_ok(nid); 311 | return -E_OK; 312 | case 803: 313 | planner_fail_on_endstops(true); 314 | send_ok(nid); 315 | return -E_OK; 316 | case 995: 317 | enable_break_on_probe(false); 318 | send_ok(nid); 319 | return -E_OK; 320 | case 996: 321 | enable_break_on_probe(true); 322 | send_ok(nid); 323 | return -E_OK; 324 | case 997: { 325 | int32_t x[3] = {0}; 326 | moves_common_set_position(x); 327 | send_ok(nid); 328 | return -E_OK; 329 | } 330 | case 999: 331 | system_reboot(); 332 | // for debug cases 333 | send_ok(nid); 334 | return -E_OK; 335 | default: 336 | { 337 | char buf[60]; 338 | snprintf(buf, 60, "unknown command M%i", cmds[0].val_i); 339 | send_error(nid, buf); 340 | planner_lock(); 341 | return -E_INCORRECT; 342 | } 343 | } 344 | break; 345 | default: 346 | { 347 | char buf[60]; 348 | snprintf(buf, 60, "unknown command %c%i", cmds[0].type, cmds[0].val_i); 349 | send_error(nid, buf); 350 | planner_lock(); 351 | return -E_INCORRECT; 352 | } 353 | } 354 | planner_lock(); 355 | return -E_INCORRECT; 356 | } 357 | 358 | int execute_g_command(const unsigned char *command, ssize_t len) 359 | { 360 | gcode_frame_t frame; 361 | int rc; 362 | 363 | if (len < 0) 364 | len = strlen((const char *)command); 365 | 366 | rc = parse_cmdline(command, len, &frame); 367 | switch (rc) 368 | { 369 | case -E_CRC: 370 | send_error(-1, "CRC error"); 371 | return rc; 372 | case -E_OK: 373 | return handle_g_command(&frame); 374 | default: 375 | { 376 | planner_lock(); 377 | char buf[320] = {0}; 378 | snprintf(buf, sizeof(buf), "parse error: %i [", (int)len); 379 | int i; 380 | for (i = 0; i < len; i++) 381 | { 382 | char cbuf[10] = {0}; 383 | sprintf(cbuf, "%02x", command[i]); 384 | strcat(buf, cbuf); 385 | } 386 | strcat(buf, "]"); 387 | send_error(-1, buf); 388 | return rc; 389 | } 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /core/control/commands/gcode_handler/gcode_handler.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | int execute_g_command(const unsigned char *command, ssize_t len); 8 | -------------------------------------------------------------------------------- /core/control/commands/status/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(status STATIC print_status.c) 3 | target_include_directories(status PUBLIC .) 4 | target_link_libraries(status moves planner ioqueue output) 5 | -------------------------------------------------------------------------------- /core/control/commands/status/print_status.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define min(a,b) ((a) < (b) ? (a) : (b)) 12 | 13 | void print_endstops(int nid) 14 | { 15 | char buf[128]; 16 | send_started(nid); 17 | int q = empty_slots(); 18 | cnc_endstops stops = moves_get_endstops(); 19 | snprintf(buf, sizeof(buf), "completed N:%i Q:%i EX:%i EY:%i EZ:%i EP:%i", nid, q, stops.stop_x, stops.stop_y, stops.stop_z, stops.probe); 20 | buf[127] = 0; 21 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 22 | } 23 | 24 | void print_position(int nid) 25 | { 26 | char buf[128]; 27 | send_started(nid); 28 | int q = empty_slots(); 29 | 30 | snprintf(buf, sizeof(buf), "completed N:%i Q:%i X:%ld Y:%ld Z:%ld", nid, q, (long)position.pos[0], (long)position.pos[1], (long)position.pos[2]); 31 | buf[127] = 0; 32 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /core/control/commands/status/print_status.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | void print_position(int nid); 5 | 6 | void print_endstops(int nid); 7 | 8 | void double2fixed(double x, int *xh, int *xl, char *sign); 9 | 10 | -------------------------------------------------------------------------------- /core/control/control.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void cb_send_queued(int nid) 8 | { 9 | send_queued(nid); 10 | } 11 | 12 | static void cb_send_started(int nid) 13 | { 14 | send_started(nid); 15 | } 16 | 17 | static void cb_send_completed(int nid) 18 | { 19 | send_completed(nid); 20 | } 21 | 22 | static void cb_send_dropped(int nid) 23 | { 24 | send_dropped(nid); 25 | } 26 | 27 | static void cb_send_failed(int nid) 28 | { 29 | send_failed(nid); 30 | } 31 | 32 | static void cb_send_completed_with_pos(int nid, const int32_t *pos) 33 | { 34 | send_completed_with_pos(nid, pos); 35 | } 36 | 37 | void init_control(steppers_definition *pd, gpio_definition *gd) 38 | { 39 | init_planner(pd, gd, cb_send_queued, cb_send_started, cb_send_completed, cb_send_completed_with_pos, cb_send_dropped, cb_send_failed); 40 | system_init(pd->reboot); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /core/control/control.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | #include 4 | #include 5 | 6 | void init_control(steppers_definition *pd, gpio_definition *gd); 7 | 8 | -------------------------------------------------------------------------------- /core/control/ioqueue/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(ioqueue STATIC print_events.c) 3 | target_include_directories(ioqueue PUBLIC .) 4 | target_link_libraries(ioqueue moves planner output) 5 | -------------------------------------------------------------------------------- /core/control/ioqueue/print_events.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #define min(a,b) ((a) < (b) ? (a) : (b)) 9 | 10 | void send_queued(int nid) 11 | { 12 | char buf[50]; 13 | int q = empty_slots(); 14 | snprintf(buf, sizeof(buf), "queued N:%i Q:%i", nid, q); 15 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 16 | } 17 | 18 | void send_dropped(int nid) 19 | { 20 | char buf[50]; 21 | int q = empty_slots(); 22 | snprintf(buf, sizeof(buf), "dropped N:%i Q:%i", nid, q); 23 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 24 | } 25 | 26 | void send_started(int nid) 27 | { 28 | char buf[50]; 29 | int q = empty_slots(); 30 | snprintf(buf, sizeof(buf), "started N:%i Q:%i", nid, q); 31 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 32 | } 33 | 34 | void send_completed(int nid) 35 | { 36 | char buf[50]; 37 | int q = empty_slots(); 38 | snprintf(buf, sizeof(buf), "completed N:%i Q:%i", nid, q); 39 | buf[49] = 0; 40 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 41 | } 42 | 43 | void send_completed_with_pos(int nid, const int32_t *pos) 44 | { 45 | char buf[50]; 46 | int q = empty_slots(); 47 | snprintf(buf, sizeof(buf), "completed N:%i Q:%i X:%ld Y:%ld Z:%ld", nid, q, (long)pos[0], (long)pos[1], (long)pos[2]); 48 | buf[49] = 0; 49 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 50 | } 51 | 52 | void send_failed(int nid) 53 | { 54 | char buf[50]; 55 | snprintf(buf, sizeof(buf), "failed N:%i move failed", nid); 56 | buf[49] = 0; 57 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 58 | } 59 | 60 | void send_ok(int nid) 61 | { 62 | send_queued(nid); 63 | send_started(nid); 64 | send_completed(nid); 65 | } 66 | 67 | void send_error(int nid, const char *err) 68 | { 69 | char buf[50]; 70 | snprintf(buf, sizeof(buf), "error N:%i %s", nid, err); 71 | buf[49] = 0; 72 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 73 | } 74 | 75 | void send_warning(int nid, const char *err) 76 | { 77 | char buf[50]; 78 | snprintf(buf, sizeof(buf), "warning N:%i %s", nid, err); 79 | buf[49] = 0; 80 | output_control_write(buf, min(strlen(buf), sizeof(buf))); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /core/control/ioqueue/print_events.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | void send_queued(int nid); 7 | void send_started(int nid); 8 | void send_completed(int nid); 9 | void send_completed_with_pos(int nid, const int32_t *pos); 10 | void send_dropped(int nid); 11 | void send_failed(int nid); 12 | 13 | void send_ok(int nid); 14 | void send_error(int nid, const char *err); 15 | void send_warning(int nid, const char *err); 16 | -------------------------------------------------------------------------------- /core/control/moves/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(moves STATIC moves.c) 2 | 3 | target_include_directories(moves PUBLIC .) 4 | 5 | target_link_libraries(moves PUBLIC m err moves_line moves_arc) 6 | 7 | add_subdirectory(moves_common) 8 | add_subdirectory(moves_line) 9 | add_subdirectory(moves_arc) 10 | 11 | if (DEBUG) 12 | add_subdirectory(unit) 13 | endif() 14 | 15 | -------------------------------------------------------------------------------- /core/control/moves/moves.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static enum { 6 | MOVE_NONE = 0, 7 | MOVE_LINE, 8 | MOVE_ARC, 9 | } current_move_type; 10 | 11 | static bool ready = true; 12 | 13 | void moves_break(void) 14 | { 15 | current_move_type = MOVE_NONE; 16 | } 17 | 18 | void moves_init(const steppers_definition *definition) 19 | { 20 | current_move_type = MOVE_NONE; 21 | moves_common_init(definition); 22 | moves_common_reset(); 23 | } 24 | 25 | void moves_reset(void) 26 | { 27 | current_move_type = MOVE_NONE; 28 | moves_common_reset(); 29 | } 30 | 31 | int moves_line_to(line_plan *plan) 32 | { 33 | ready = true; 34 | current_move_type = MOVE_LINE; 35 | return line_move_to(plan); 36 | } 37 | 38 | int moves_arc_to(arc_plan *plan) 39 | { 40 | ready = true; 41 | current_move_type = MOVE_ARC; 42 | return arc_move_to(plan); 43 | } 44 | 45 | int32_t moves_step_tick(void) 46 | { 47 | /* Check endstops */ 48 | bool es; 49 | if (current_move_type == MOVE_LINE) 50 | { 51 | es = line_check_endstops(); 52 | } 53 | else if (current_move_type == MOVE_ARC) 54 | { 55 | es = arc_check_endstops(); 56 | } 57 | if (es) 58 | { 59 | moves_common_endstops_touched(); 60 | return -1; 61 | } 62 | 63 | if (ready) 64 | { 65 | /* Normal movement */ 66 | int res; 67 | if (current_move_type == MOVE_LINE) 68 | { 69 | res = line_step_tick(); 70 | } 71 | else if (current_move_type == MOVE_ARC) 72 | { 73 | res = arc_step_tick(); 74 | } 75 | 76 | if (res == -E_OK) 77 | { 78 | double len; 79 | double dt; 80 | ready = moves_common_make_steps(&len); 81 | if (current_move_type == MOVE_LINE) 82 | { 83 | dt = line_acceleration_process(len); 84 | } 85 | else if (current_move_type == MOVE_ARC) 86 | { 87 | dt = arc_acceleration_process(len); 88 | } 89 | return dt * 1000000UL; 90 | } 91 | else if (res == -E_NEXT) 92 | { 93 | moves_common_line_finished(); 94 | return -1; 95 | } 96 | } 97 | else 98 | { 99 | /* Move to target position */ 100 | double len, dt; 101 | ready = moves_common_make_steps(&len); 102 | if (current_move_type == MOVE_LINE) 103 | { 104 | dt = len / line_movement_feed(); 105 | } 106 | else if (current_move_type == MOVE_ARC) 107 | { 108 | dt = len / arc_movement_feed(); 109 | } 110 | return dt * 1000000UL; 111 | } 112 | return -1; 113 | } 114 | 115 | cnc_endstops moves_get_endstops(void) 116 | { 117 | return moves_common_def.get_endstops(); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /core/control/moves/moves.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | void moves_init(const steppers_definition *definition); 10 | void moves_reset(void); 11 | void moves_break(void); 12 | 13 | int moves_line_to(line_plan *plan); 14 | int moves_arc_to(arc_plan *plan); 15 | 16 | int32_t moves_step_tick(void); 17 | 18 | cnc_endstops moves_get_endstops(void); 19 | 20 | -------------------------------------------------------------------------------- /core/control/moves/moves_arc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(moves_arc STATIC arc.c) 2 | 3 | target_include_directories(moves_arc PUBLIC .) 4 | 5 | target_link_libraries(moves_arc PUBLIC m err moves_common) 6 | 7 | -------------------------------------------------------------------------------- /core/control/moves/moves_arc/arc.c: -------------------------------------------------------------------------------- 1 | #define __STDC_WANT_DEC_FP__ 2 | #include 3 | #include 4 | #include 5 | //#include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define SQR(a) ((a) * (a)) 12 | const double pi = 3.1415926535; 13 | 14 | #define COS_RESYNC_FRQ 8 15 | 16 | // Running 17 | 18 | static arc_plan *current_plan; 19 | 20 | static struct arc_state_s 21 | { 22 | double t; 23 | double tv; 24 | double cost; 25 | double sint; 26 | 27 | struct { 28 | int32_t x; 29 | int32_t y; 30 | int32_t z; 31 | } plane; 32 | 33 | struct { 34 | int32_t position[3]; 35 | } global; 36 | 37 | int8_t steps[3]; 38 | 39 | int32_t dir[3]; 40 | acceleration_state acc; 41 | } current_state; 42 | 43 | static void global_update_position(struct arc_state_s *state) 44 | { 45 | switch (current_plan->plane) 46 | { 47 | case XY: // XY 48 | state->global.position[0] = state->plane.x; 49 | state->global.position[1] = state->plane.y; 50 | state->global.position[2] = state->plane.z; 51 | break; 52 | 53 | case YZ: // YZ 54 | state->global.position[0] = state->plane.z; 55 | state->global.position[1] = state->plane.x; 56 | state->global.position[2] = state->plane.y; 57 | break; 58 | 59 | case ZX: // ZX 60 | state->global.position[0] = state->plane.y; 61 | state->global.position[1] = state->plane.z; 62 | state->global.position[2] = state->plane.x; 63 | break; 64 | } 65 | } 66 | 67 | static bool iterate(arc_plan *plan, struct arc_state_s *state) 68 | { 69 | if (state->plane.x == plan->x2[0] && 70 | state->plane.y == plan->x2[1] && 71 | state->plane.z == plan->h) 72 | { 73 | return false; 74 | } 75 | 76 | float dxdt = -plan->a * state->sint; 77 | float dydt = plan->b * state->cost; 78 | float dzdt = plan->h; 79 | 80 | float maxddt = fmax(fmax(fabs(dxdt), fabs(dydt)), fabs(dzdt)); 81 | 82 | if (plan->t_end > plan->t_start && state->t > plan->t_end - 1e-6) 83 | return false; 84 | if (plan->t_end < plan->t_start && state->t < plan->t_end + 1e-6) 85 | return false; 86 | 87 | float dt = fmin(1/maxddt, fabs(state->t - plan->t_end)); 88 | if (plan->cw) 89 | dt = -dt; 90 | 91 | state->t += dt; 92 | state->tv += dt; 93 | if (state->tv >= pi/COS_RESYNC_FRQ) 94 | { 95 | state->tv -= pi/COS_RESYNC_FRQ; 96 | state->cost = cos(state->t); 97 | state->sint = sin(state->t); 98 | } 99 | else if (current_state.tv < 0) 100 | { 101 | state->tv += pi/COS_RESYNC_FRQ; 102 | state->cost = cos(state->t); 103 | state->sint = sin(state->t); 104 | } 105 | else 106 | { 107 | double cost = state->cost - dt * state->sint - dt*dt/2*state->cost; 108 | double sint = state->sint + dt * state->cost - dt*dt/2*state->sint; 109 | 110 | state->cost = cost; 111 | state->sint = sint; 112 | } 113 | 114 | state->plane.x = round(plan->a * state->cost); 115 | state->plane.y = round(plan->b * state->sint); 116 | state->plane.z = round(plan->h * (state->t - plan->t_start)); 117 | 118 | int i; 119 | int32_t oldpos[3]; 120 | for (i = 0; i < 3; i++) 121 | oldpos[i] = state->global.position[i]; 122 | global_update_position(state); 123 | for (i = 0; i < 3; i++) 124 | state->steps[i] = state->global.position[i] - oldpos[i]; 125 | return true; 126 | } 127 | 128 | // module API functions 129 | bool arc_check_endstops(void) 130 | { 131 | if (current_plan->check_break && current_plan->check_break(current_state.dir, current_plan->check_break_data)) 132 | return true; 133 | return false; 134 | } 135 | 136 | int arc_step_tick(void) 137 | { 138 | int i; 139 | if (!iterate(current_plan, ¤t_state)) 140 | return -E_NEXT; 141 | 142 | // make steps 143 | for (i = 0; i < 3; i++) 144 | { 145 | int d = current_state.steps[i]; 146 | moves_common_schedule_step(i, d); 147 | } 148 | return -E_OK; 149 | } 150 | 151 | double arc_acceleration_process(double len) 152 | { 153 | double dt = len / current_state.acc.feed; 154 | current_state.acc.current_t = current_state.t; 155 | acceleration_process(¤t_state.acc, dt, current_state.acc.current_t); 156 | return dt; 157 | } 158 | 159 | double arc_movement_feed(void) 160 | { 161 | return current_state.acc.feed; 162 | } 163 | 164 | static void arc_init_move(arc_plan *plan, struct arc_state_s *state) 165 | { 166 | state->t = plan->t_start; 167 | state->cost = cos(state->t); 168 | state->sint = sin(state->t); 169 | 170 | state->tv = plan->t_start; 171 | while (state->tv >= pi/COS_RESYNC_FRQ) 172 | state->tv -= pi/COS_RESYNC_FRQ; 173 | while (state->tv < 0) 174 | state->tv += pi/COS_RESYNC_FRQ; 175 | 176 | state->plane.x = plan->x1[0]; 177 | state->plane.y = plan->x1[1]; 178 | state->plane.z = 0; 179 | 180 | global_update_position(state); 181 | // printf("=================\nstart %lf %lf\n", plan->t_start, plan->t_end); 182 | // printf("Start: %i %i : %i %i : %i %i\n", (int)plan->x2[0], (int)plan->x2[1], (int)state->plane_current.x, (int)state->plane_current.y, (int)state->plane_target.x, (int)state->plane_target.y); 183 | } 184 | 185 | int arc_move_to(arc_plan *plan) 186 | { 187 | current_plan = plan; 188 | if (!plan->ready) 189 | { 190 | arc_pre_calculate(plan); 191 | } 192 | 193 | current_state.acc.acceleration = current_plan->acceleration; 194 | current_state.acc.feed = current_plan->feed0; 195 | current_state.acc.target_feed = current_plan->feed; 196 | current_state.acc.end_feed = current_plan->feed1; 197 | current_state.acc.type = STATE_ACC; 198 | 199 | current_state.acc.current_t = current_plan->t_start; 200 | current_state.acc.start_t = current_plan->t_start; 201 | current_state.acc.end_t = current_plan->t_end; 202 | current_state.acc.acc_t = current_plan->t_acc; 203 | current_state.acc.dec_t = current_plan->t_dec; 204 | 205 | arc_init_move(plan, ¤t_state); 206 | 207 | moves_common_line_started(); 208 | return -E_OK; 209 | } 210 | 211 | void arc_pre_calculate(arc_plan *arc) 212 | { 213 | double stpu_x; 214 | double stpu_y; 215 | double stpu_z; 216 | 217 | double x1, y1; 218 | double x2, y2; 219 | double H; 220 | 221 | x1 = arc->x1[0]; 222 | y1 = arc->x1[1]; 223 | x2 = arc->x2[0]; 224 | y2 = arc->x2[1]; 225 | H = arc->H; 226 | 227 | switch (arc->plane) 228 | { 229 | case XY: 230 | stpu_x = moves_common_def.steps_per_unit[0]; 231 | stpu_y = moves_common_def.steps_per_unit[1]; 232 | stpu_z = moves_common_def.steps_per_unit[2]; 233 | break; 234 | case YZ: 235 | stpu_x = moves_common_def.steps_per_unit[1]; 236 | stpu_y = moves_common_def.steps_per_unit[2]; 237 | stpu_z = moves_common_def.steps_per_unit[0]; 238 | break; 239 | case ZX: 240 | stpu_x = moves_common_def.steps_per_unit[2]; 241 | stpu_y = moves_common_def.steps_per_unit[0]; 242 | stpu_z = moves_common_def.steps_per_unit[1]; 243 | break; 244 | } 245 | 246 | arc->t_start = atan2(y1, x1); 247 | arc->t_end = atan2(y2, x2); 248 | if (arc->cw) 249 | { 250 | while (arc->t_end > arc->t_start) 251 | arc->t_end -= 2*pi; 252 | } 253 | else 254 | { 255 | while (arc->t_end < arc->t_start) 256 | arc->t_end += 2*pi; 257 | } 258 | 259 | arc->h = H / (arc->t_end - arc->t_start); 260 | arc->cost_start = cos(arc->t_start); 261 | arc->sint_start = sin(arc->t_start); 262 | 263 | /* check feeds */ 264 | if (arc->feed < moves_common_def.feed_base) 265 | arc->feed = moves_common_def.feed_base; 266 | else if (moves_common_def.feed_max > 0 && arc->feed > moves_common_def.feed_max) 267 | arc->feed = moves_common_def.feed_max; 268 | 269 | if (arc->feed1 < moves_common_def.feed_base) 270 | arc->feed1 = moves_common_def.feed_base; 271 | else if (arc->feed1 > arc->feed) 272 | arc->feed1 = arc->feed; 273 | 274 | if (arc->feed0 < moves_common_def.feed_base) 275 | arc->feed0 = moves_common_def.feed_base; 276 | else if (arc->feed0 > arc->feed) 277 | arc->feed0 = arc->feed; 278 | 279 | /* calculate acceleration and deceleration */ 280 | arc->t_acc = acceleration(arc->feed0, arc->feed, arc->acceleration, arc->len, arc->t_start, arc->t_end); 281 | arc->t_dec = acceleration(arc->feed1, arc->feed, arc->acceleration, arc->len, arc->t_end, arc->t_start); 282 | 283 | arc->ready = 1; 284 | } 285 | 286 | -------------------------------------------------------------------------------- /core/control/moves/moves_arc/arc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef enum { 8 | XY = 0, 9 | YZ, 10 | ZX, 11 | } arc_plane; 12 | 13 | typedef struct { 14 | // Specified data 15 | arc_plane plane; // selected plane 16 | double x1[2]; // start point in local crds 17 | double x2[2]; // end point in local crds 18 | double H; // height of helix 19 | double a, b; 20 | double feed; // feed of moving 21 | double feed0; // initial feed 22 | double feed1; // finishing feed 23 | uint32_t acceleration; // acceleration 24 | 25 | int (*check_break)(int32_t *dx, void *user_arg); 26 | void *check_break_data; 27 | 28 | // Pre-calculated data 29 | double len; // arc length 30 | double t_acc; // steps on acceleration 31 | double t_dec; // steps on deceleration 32 | double t_start, t_end; 33 | double cost_start, sint_start; 34 | double h; 35 | 36 | // flags 37 | struct { 38 | int cw : 1; // True if clock-wise 39 | int ready : 1; // Plan is calculated 40 | }; 41 | } arc_plan; 42 | 43 | void arc_init ( steppers_definition definition ); 44 | 45 | void arc_pre_calculate ( arc_plan *arc ); 46 | 47 | int arc_move_to(arc_plan *plan); 48 | 49 | int arc_step_tick(void); 50 | double arc_movement_feed(void); 51 | double arc_acceleration_process(double len); 52 | bool arc_check_endstops(void); 53 | 54 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(moves_common STATIC common.c acceleration.c) 2 | 3 | target_include_directories(moves_common PUBLIC .) 4 | 5 | target_link_libraries(moves_common PUBLIC m err) 6 | 7 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/Makefile: -------------------------------------------------------------------------------- 1 | CC := $(CC) -I./ 2 | 3 | all: libmoves_common.a 4 | 5 | %.o : %.c 6 | $(CC) -c $< -o $@ 7 | 8 | libmoves_common.a: common.o acceleration.o 9 | $(AR) rcs $@ $^ 10 | $(MAKE) clean 11 | 12 | clean: 13 | rm *.o 14 | 15 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/acceleration.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void acceleration_process(acceleration_state *state, double step_delay, double t) 6 | { 7 | double cur_dt = fabs(state->current_t - state->start_t); 8 | double acc_dt = fabs(state->acc_t - state->start_t); 9 | double dec_dt = fabs(state->dec_t - state->start_t); 10 | double total_dt = fabs(state->end_t - state->start_t); 11 | 12 | state->current_t = t; 13 | if (cur_dt >= total_dt) 14 | { 15 | state->type = STATE_STOP; 16 | return; 17 | } 18 | 19 | 20 | switch (state->type) 21 | { 22 | case STATE_ACC: 23 | { 24 | if (cur_dt >= acc_dt) 25 | { 26 | if (cur_dt < dec_dt) 27 | { 28 | state->type = STATE_GO; 29 | state->feed = state->target_feed; 30 | } 31 | else 32 | { 33 | state->type = STATE_DEC; 34 | } 35 | } 36 | else 37 | { 38 | state->feed = accelerate(state->feed, state->acceleration, step_delay); 39 | } 40 | break; 41 | } 42 | case STATE_GO: 43 | if (cur_dt >= dec_dt) 44 | { 45 | state->type = STATE_DEC; 46 | } 47 | break; 48 | case STATE_DEC: 49 | { 50 | state->feed = accelerate(state->feed, -state->acceleration, step_delay); 51 | if (state->feed < state->end_feed) 52 | { 53 | state->feed = state->end_feed; 54 | state->type = STATE_STOP_COMPLETION; 55 | } 56 | break; 57 | } 58 | case STATE_STOP: 59 | case STATE_STOP_COMPLETION: 60 | break; 61 | } 62 | } 63 | 64 | // Find new feed when acceleration 65 | // 66 | // feed. mm / sec 67 | // acc. mm / sec^2 68 | // delay. sec 69 | // 70 | // Return: new feed in mm / min 71 | double accelerate(double feed, double acc, double delay) 72 | { 73 | double df = acc * delay; 74 | return feed + df; 75 | } 76 | 77 | // Find amount of acceleration steps from feed0 to feed1 78 | // 79 | // feed0. mm / sec 80 | // feed1. mm / sec 81 | // acc. mm / sec^2 82 | double acceleration(double feed0, 83 | double feed1, 84 | double acc, 85 | double len, 86 | double t_start, 87 | double t_end) 88 | { 89 | double mlen = (feed1*feed1 - feed0*feed0) / (2*acc); 90 | return t_start + (t_end - t_start) * mlen / len; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/acceleration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef enum { 6 | STATE_STOP = 0, 7 | STATE_ACC, 8 | STATE_GO, 9 | STATE_DEC, 10 | STATE_STOP_COMPLETION, 11 | } acceleration_type; 12 | 13 | typedef struct { 14 | acceleration_type type; 15 | 16 | float start_t; 17 | float end_t; 18 | float acc_t; 19 | float dec_t; 20 | float current_t; 21 | 22 | double acceleration; 23 | double feed; 24 | double target_feed; 25 | double end_feed; 26 | } acceleration_state; 27 | 28 | void acceleration_process(acceleration_state *state, double step_delay, double current_t); 29 | 30 | double accelerate(double feed, double acc, double delay); 31 | double acceleration(double feed0, 32 | double feed1, 33 | double acc, 34 | double len, 35 | double begin, 36 | double end); 37 | 38 | 39 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define SQR(a) ((a) * (a)) 8 | 9 | static double moves_len[2][2][2] = {}; 10 | 11 | cnc_position position; 12 | steppers_definition moves_common_def; 13 | 14 | void moves_common_init(const steppers_definition *definition) 15 | { 16 | memcpy(&moves_common_def, definition, sizeof(moves_common_def)); 17 | int x, y, z; 18 | for (z = 0; z < 2; z++) 19 | for (y = 0; y < 2; y++) 20 | for (x = 0; x < 2; x++) 21 | { 22 | double sx = x/moves_common_def.steps_per_unit[0]; 23 | double sy = y/moves_common_def.steps_per_unit[1]; 24 | double sz = z/moves_common_def.steps_per_unit[2]; 25 | moves_len[z][y][x] = sqrt(sx*sx + sy*sy + sz*sz); 26 | } 27 | } 28 | 29 | void moves_common_reset(void) 30 | { 31 | int i; 32 | for (i = 0; i < 3; i++) 33 | { 34 | position.target_pos[i] = 0; 35 | position.pos[i] = 0; 36 | } 37 | } 38 | 39 | // Find delay between ticks 40 | // 41 | // feed. mm / sec 42 | // len. mm 43 | // 44 | // Return: delay. sec 45 | double feed2delay(double feed, double step_len) 46 | { 47 | if (feed < 0.001) 48 | feed = 0.001; 49 | return step_len / feed; 50 | } 51 | 52 | 53 | // Movement functions 54 | void moves_common_set_dir(int i, bool dir) 55 | { 56 | if (dir) 57 | position.dir[i] = 1; 58 | else 59 | position.dir[i] = -1; 60 | if (moves_common_def.set_dir) 61 | moves_common_def.set_dir(i, dir); 62 | } 63 | 64 | void moves_common_schedule_step(int i, int dir) 65 | { 66 | position.target_pos[i] += dir; 67 | } 68 | 69 | void moves_common_make_step(int i) 70 | { 71 | if (moves_common_def.make_step) 72 | moves_common_def.make_step(i); 73 | } 74 | 75 | static int clip(int d) 76 | { 77 | if (d < 0) 78 | return -1; 79 | if (d > 0) 80 | return 1; 81 | return 0; 82 | } 83 | 84 | bool moves_common_make_steps(double *len) 85 | { 86 | int i; 87 | bool ready = true; 88 | int8_t delta[3]; 89 | for (i = 0; i < 3; i++) 90 | { 91 | delta[i] = position.target_pos[i] - position.pos[i]; 92 | int d = clip(delta[i]); 93 | if (d != delta[i]) 94 | ready = false; 95 | moves_common_set_dir(i, d >= 0); 96 | if (d != 0) 97 | { 98 | moves_common_make_step(i); 99 | position.pos[i] += d; 100 | } 101 | } 102 | if (len != NULL) 103 | *len = moves_common_step_len(delta[0], delta[1], delta[2]); 104 | return ready; 105 | } 106 | 107 | void moves_common_line_started(void) 108 | { 109 | if (moves_common_def.line_started) 110 | moves_common_def.line_started(); 111 | } 112 | 113 | void moves_common_endstops_touched(void) 114 | { 115 | if (moves_common_def.endstops_touched) 116 | moves_common_def.endstops_touched(); 117 | } 118 | 119 | void moves_common_line_finished(void) 120 | { 121 | if (moves_common_def.line_finished) 122 | moves_common_def.line_finished(); 123 | } 124 | 125 | // State 126 | void moves_common_set_position(const int32_t *x) 127 | { 128 | int i; 129 | for (i = 0; i < 3; i++) 130 | { 131 | position.pos[i] = x[i]; 132 | position.target_pos[i] = x[i]; 133 | } 134 | } 135 | 136 | 137 | double moves_common_step_len(int8_t dx, int8_t dy, int8_t dz) 138 | { 139 | dx = (dx != 0); 140 | dy = (dy != 0); 141 | dz = (dz != 0); 142 | return moves_len[dz][dy][dx]; 143 | } 144 | 145 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | typedef struct { 10 | uint8_t en:1; 11 | uint8_t dir:1; 12 | } step_flags; 13 | 14 | typedef struct { 15 | int32_t target_pos[3]; 16 | int32_t pos[3]; 17 | int dir[3]; 18 | double speed[3]; 19 | step_flags flags[3]; 20 | } cnc_position; 21 | 22 | // Math functions 23 | double feed2delay(double feed, double step_len); 24 | 25 | void moves_common_init(const steppers_definition *definition); 26 | void moves_common_reset(void); 27 | 28 | // Movement functions 29 | void moves_common_set_dir(int i, bool dir); 30 | void moves_common_make_step(int i); 31 | 32 | void moves_common_schedule_step(int i, int dir); 33 | bool moves_common_make_steps(double *len); 34 | 35 | void moves_common_line_started(void); 36 | void moves_common_endstops_touched(void); 37 | void moves_common_line_finished(void); 38 | 39 | double moves_common_step_len(int8_t dx, int8_t dy, int8_t dz); 40 | 41 | // State 42 | void moves_common_set_position(const int32_t *x); 43 | 44 | extern cnc_position position; 45 | 46 | extern steppers_definition moves_common_def; 47 | 48 | -------------------------------------------------------------------------------- /core/control/moves/moves_common/steppers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | uint8_t stop_x:1; 8 | uint8_t stop_y:1; 9 | uint8_t stop_z:1; 10 | uint8_t probe:1; 11 | } cnc_endstops; 12 | 13 | typedef struct 14 | { 15 | void (*reboot)(void); 16 | void (*set_dir)(int i, bool dir); 17 | void (*make_step)(int i); 18 | void (*enable_step)(bool en); 19 | void (*line_started)(void); 20 | void (*line_finished)(void); 21 | void (*line_error)(void); 22 | void (*endstops_touched)(void); 23 | cnc_endstops (*get_endstops)(void); 24 | double steps_per_unit[3]; // steps / mm 25 | double feed_base; // mm / sec 26 | double feed_max; // mm / sec 27 | double acc_default; // mm / sec^2 28 | double feed_default; // mm / sec 29 | bool configured; 30 | } steppers_definition; 31 | 32 | 33 | -------------------------------------------------------------------------------- /core/control/moves/moves_line/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(moves_line STATIC line.c) 2 | 3 | target_include_directories(moves_line PUBLIC .) 4 | 5 | target_link_libraries(moves_line PUBLIC m err moves_common) 6 | 7 | -------------------------------------------------------------------------------- /core/control/moves/moves_line/line.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define SQR(x) ((x) * (x)) 10 | 11 | static line_plan *current_plan; 12 | 13 | static struct 14 | { 15 | int8_t dir[3]; 16 | int32_t steps[3]; 17 | int32_t err[3]; 18 | int is_moving; 19 | 20 | acceleration_state acc; 21 | 22 | int32_t start_pos[3]; 23 | } current_state; 24 | 25 | int line_move_to(line_plan *plan) 26 | { 27 | int i; 28 | current_plan = plan; 29 | 30 | if (current_plan->len < 0) 31 | { 32 | line_pre_calculate(current_plan); 33 | } 34 | 35 | for (i = 0; i < 3; i++) 36 | { 37 | if (current_plan->x[i] > 0) 38 | current_state.dir[i] = 1; 39 | else if (current_plan->x[i] < 0) 40 | current_state.dir[i] = -1; 41 | else 42 | current_state.dir[i] = 0; 43 | current_state.steps[i] = 0; 44 | } 45 | 46 | if (current_plan->steps == 0) 47 | return -E_NEXT; 48 | 49 | current_state.acc.acceleration = current_plan->acceleration; 50 | current_state.acc.feed = current_plan->feed0; 51 | current_state.acc.target_feed = current_plan->feed; 52 | current_state.acc.end_feed = current_plan->feed1; 53 | current_state.acc.type = STATE_ACC; 54 | 55 | current_state.acc.current_t = 0; 56 | current_state.acc.start_t = 0; 57 | current_state.acc.end_t = current_plan->steps; 58 | current_state.acc.acc_t = current_plan->acc_steps; 59 | current_state.acc.dec_t = current_plan->dec_steps; 60 | current_state.is_moving = 1; 61 | for (i = 0; i < 3; i++) 62 | { 63 | current_state.err[i] = 0; 64 | current_state.start_pos[i] = position.pos[i]; 65 | } 66 | 67 | moves_common_line_started(); 68 | return -E_OK; 69 | } 70 | 71 | static bool make_step(void) 72 | { 73 | int i; 74 | if (current_state.acc.current_t >= current_state.acc.end_t) 75 | { 76 | return false; 77 | } 78 | 79 | /* Bresenham */ 80 | moves_common_schedule_step(current_plan->maxi, current_state.dir[current_plan->maxi]); 81 | 82 | for (i = 0; i < 3; i++) 83 | { 84 | if (i == current_plan->maxi) 85 | continue; 86 | current_state.err[i] += abs(current_plan->x[i]); 87 | if (current_state.err[i] * 2 >= (int32_t)current_plan->steps) 88 | { 89 | current_state.err[i] -= (int32_t)current_plan->steps; 90 | moves_common_schedule_step(i, current_state.dir[i]); 91 | } 92 | } 93 | return true; 94 | } 95 | 96 | bool line_check_endstops(void) 97 | { 98 | // Check for endstops 99 | if (current_plan->check_break && current_plan->check_break(current_plan->x, current_plan->check_break_data)) 100 | { 101 | return true; 102 | } 103 | return false; 104 | } 105 | 106 | int line_step_tick(void) 107 | { 108 | // Make step 109 | if (!make_step()) 110 | { 111 | return -E_NEXT; 112 | } 113 | 114 | /* Calculating delay */ 115 | current_state.acc.current_t++; 116 | return -E_OK; 117 | } 118 | 119 | double line_movement_feed(void) 120 | { 121 | return current_state.acc.feed; 122 | } 123 | 124 | double line_acceleration_process(double len) 125 | { 126 | double dt = len / current_state.acc.feed; 127 | acceleration_process(¤t_state.acc, dt, current_state.acc.current_t); 128 | return dt; 129 | } 130 | 131 | static void bresenham_plan(line_plan *plan) 132 | { 133 | int i; 134 | plan->steps = abs(plan->x[0]); 135 | plan->maxi = 0; 136 | if (abs(plan->x[1]) > plan->steps) 137 | { 138 | plan->maxi = 1; 139 | plan->steps = abs(plan->x[1]); 140 | } 141 | 142 | if (abs(plan->x[2]) > plan->steps) 143 | { 144 | plan->maxi = 2; 145 | plan->steps = abs(plan->x[2]); 146 | } 147 | } 148 | 149 | void line_pre_calculate(line_plan *line) 150 | { 151 | int j; 152 | double l = 0; 153 | for (j = 0; j < 3; j++) 154 | { 155 | double d = line->x[j] / moves_common_def.steps_per_unit[j]; 156 | l += d*d; 157 | } 158 | line->len = sqrt(l); 159 | 160 | if (line->len == 0) 161 | return; 162 | 163 | if (line->feed < moves_common_def.feed_base) 164 | line->feed = moves_common_def.feed_base; 165 | else if (moves_common_def.feed_max > 0 && line->feed > moves_common_def.feed_max) 166 | line->feed = moves_common_def.feed_max; 167 | 168 | if (line->feed1 < moves_common_def.feed_base) 169 | line->feed1 = moves_common_def.feed_base; 170 | else if (line->feed1 > line->feed) 171 | line->feed1 = line->feed; 172 | 173 | if (line->feed0 < moves_common_def.feed_base) 174 | line->feed0 = moves_common_def.feed_base; 175 | else if (line->feed0 > line->feed) 176 | line->feed0 = line->feed; 177 | 178 | bresenham_plan(line); 179 | line->acc_steps = acceleration(line->feed0, line->feed, line->acceleration, line->len, 0, line->steps); 180 | line->dec_steps = acceleration(line->feed1, line->feed, line->acceleration, line->len, line->steps, 0); 181 | 182 | if (line->acc_steps + line->dec_steps > line->steps) 183 | { 184 | int32_t d = (line->acc_steps + line->dec_steps - line->steps) / 2; 185 | line->acc_steps -= d; 186 | line->dec_steps -= d; 187 | if (line->acc_steps + line->dec_steps < line->steps) 188 | line->acc_steps += (line->steps - line->acc_steps - line->dec_steps); 189 | } 190 | if (line->acc_steps < 0 || line->dec_steps < 0) 191 | { 192 | // We can not perform such moving! 193 | if (line->acc_steps < 0) 194 | line->acc_steps = 0; 195 | if (line->dec_steps < 0) 196 | line->dec_steps = 0; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /core/control/moves/moves_line/line.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | typedef struct { 9 | // Specified data 10 | int32_t x[3]; // delta. steps 11 | double feed; // feed of moving. mm / sec 12 | double feed0; // initial feed. mm / sec 13 | double feed1; // finishing feed. mm / sec 14 | double acceleration; // acceleration mm / sec^2 15 | int (*check_break)(int32_t *dx, void *user_arg); 16 | void *check_break_data; 17 | 18 | // Pre-calculated data 19 | double len; // length of delta. mm 20 | int maxi; // which axis has max steps 21 | uint32_t steps; // total amount of steps 22 | uint32_t acc_steps; // steps on acceleration 23 | uint32_t dec_steps; // steps on deceleration 24 | } line_plan; 25 | 26 | // pre-calculate parameters of moving 27 | void line_pre_calculate(line_plan *line); 28 | 29 | // init line moving 30 | int line_move_to(line_plan *plan); 31 | 32 | // tick 33 | int line_step_tick(void); 34 | 35 | double line_acceleration_process(double delay); 36 | 37 | double line_movement_feed(void); 38 | 39 | bool line_check_endstops(void); 40 | 41 | -------------------------------------------------------------------------------- /core/control/moves/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_line test_line.c) 2 | target_link_libraries(test_line PUBLIC moves_line) 3 | 4 | 5 | add_executable(test_arc test_arc.c) 6 | target_link_libraries(test_arc PUBLIC moves_arc) 7 | 8 | -------------------------------------------------------------------------------- /core/control/moves/unit/test_arc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int32_t pos[3]; 5 | bool dirs[3]; 6 | 7 | void set_dir(int i, bool dir) 8 | { 9 | dirs[i] = dir; 10 | } 11 | 12 | void make_step(int i) 13 | { 14 | if (!dirs[i]) 15 | pos[i]++; 16 | else 17 | pos[i]--; 18 | } 19 | 20 | void test_1(void) 21 | { 22 | steppers_definition def = { 23 | .set_dir = set_dir, 24 | .make_step = make_step, 25 | .steps_per_unit = {1, 1, 1}, 26 | }; 27 | 28 | moves_common_init(&def); 29 | 30 | arc_plan plan = { 31 | .cw = false, 32 | .x = {0, 4, 0}, 33 | .a = 10.0, 34 | .b = 10.0, 35 | .plane = XY, 36 | .feed = 100, 37 | .feed0 = 100, 38 | .feed1 = 100, 39 | .acceleration = 40, 40 | .len = -1, // must be negative at init 41 | }; 42 | 43 | pos[0] = pos[1] = pos[2] = 0; 44 | arc_move_to(&plan); 45 | int delay = -1; 46 | do 47 | { 48 | delay = arc_step_tick(); 49 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 50 | } while (delay >= 0); 51 | } 52 | 53 | void test_2(void) 54 | { 55 | steppers_definition def = { 56 | .set_dir = set_dir, 57 | .make_step = make_step, 58 | .steps_per_unit = {1, 1, 1}, 59 | }; 60 | 61 | moves_common_init(&def); 62 | 63 | arc_plan plan = { 64 | .cw = false, 65 | .x = {0, 16, 0}, 66 | .a = 10.0, 67 | .b = 10.0, 68 | .plane = XY, 69 | .feed = 100, 70 | .feed0 = 100, 71 | .feed1 = 100, 72 | .acceleration = 40, 73 | .len = -1, // must be negative at init 74 | }; 75 | 76 | pos[0] = pos[1] = pos[2] = 0; 77 | printf("%i %i %i\n", pos[0], pos[1], pos[2]); 78 | arc_move_to(&plan); 79 | int delay = -1; 80 | do 81 | { 82 | delay = arc_step_tick(); 83 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 84 | } while (delay >= 0); 85 | } 86 | 87 | void test_3(void) 88 | { 89 | steppers_definition def = { 90 | .set_dir = set_dir, 91 | .make_step = make_step, 92 | .steps_per_unit = {1, 1, 1}, 93 | }; 94 | 95 | moves_common_init(&def); 96 | 97 | arc_plan plan = { 98 | .cw = false, 99 | .x = {0, 20, 0}, 100 | .a = 10.0, 101 | .b = 10.0, 102 | .plane = XY, 103 | .feed = 100, 104 | .feed0 = 100, 105 | .feed1 = 100, 106 | .acceleration = 40, 107 | .len = -1, // must be negative at init 108 | }; 109 | 110 | pos[0] = pos[1] = pos[2] = 0; 111 | printf("%i %i %i\n", pos[0], pos[1], pos[2]); 112 | arc_move_to(&plan); 113 | int delay = -1; 114 | do 115 | { 116 | delay = arc_step_tick(); 117 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 118 | } while (delay >= 0); 119 | } 120 | 121 | void test_4(void) 122 | { 123 | steppers_definition def = { 124 | .set_dir = set_dir, 125 | .make_step = make_step, 126 | .steps_per_unit = {1, 1, 1}, 127 | }; 128 | 129 | moves_common_init(&def); 130 | 131 | arc_plan plan = { 132 | .cw = false, 133 | .x = {40000, 0, 0}, 134 | .a = 20000.0, 135 | .b = 20000.0, 136 | .plane = XY, 137 | .feed = 100, 138 | .feed0 = 100, 139 | .feed1 = 100, 140 | .acceleration = 40, 141 | .len = -1, // must be negative at init 142 | }; 143 | 144 | pos[0] = pos[1] = pos[2] = 0; 145 | printf("%i %i %i\n", pos[0], pos[1], pos[2]); 146 | arc_move_to(&plan); 147 | int delay = -1; 148 | do 149 | { 150 | delay = arc_step_tick(); 151 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 152 | } while (delay >= 0); 153 | } 154 | 155 | 156 | int main(void) 157 | { 158 | test_4(); 159 | return 0; 160 | } 161 | 162 | 163 | -------------------------------------------------------------------------------- /core/control/moves/unit/test_line.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int32_t pos[3]; 5 | bool dirs[3]; 6 | 7 | void set_dir(int i, bool dir) 8 | { 9 | dirs[i] = dir; 10 | } 11 | 12 | void make_step(int i) 13 | { 14 | if (dirs[i]) 15 | pos[i]++; 16 | else 17 | pos[i]--; 18 | } 19 | 20 | void test_1(void) 21 | { 22 | steppers_definition def = { 23 | .set_dir = set_dir, 24 | .make_step = make_step, 25 | .steps_per_unit = {1, 1, 1}, 26 | }; 27 | 28 | moves_common_init(&def); 29 | 30 | line_plan plan = { 31 | .x = {100, 10, 0}, 32 | .feed = 100, 33 | .feed0 = 100, 34 | .feed1 = 100, 35 | .acceleration = 40, 36 | .len = -1, // must be negative at init 37 | }; 38 | 39 | pos[0] = pos[1] = pos[2] = 0; 40 | line_move_to(&plan); 41 | int delay = -1; 42 | do 43 | { 44 | delay = line_step_tick(); 45 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 46 | } while (delay > 0); 47 | } 48 | 49 | void test_2(void) 50 | { 51 | steppers_definition def = { 52 | .set_dir = set_dir, 53 | .make_step = make_step, 54 | .steps_per_unit = {1, 1, 1}, 55 | .feed_base = 0.01, 56 | }; 57 | 58 | moves_common_init(&def); 59 | 60 | line_plan plan = { 61 | .x = {100, 10, 0}, 62 | .feed = 20, 63 | .feed0 = 0, 64 | .feed1 = 0, 65 | .acceleration = 40, 66 | .len = -1, // must be negative at init 67 | }; 68 | 69 | pos[0] = pos[1] = pos[2] = 0; 70 | line_move_to(&plan); 71 | int delay = -1; 72 | do 73 | { 74 | delay = line_step_tick(); 75 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 76 | } while (delay > 0); 77 | } 78 | 79 | void test_3(void) 80 | { 81 | steppers_definition def = { 82 | .set_dir = set_dir, 83 | .make_step = make_step, 84 | .steps_per_unit = {400, 400, 400}, 85 | .feed_base = 0.01, 86 | }; 87 | 88 | moves_common_init(&def); 89 | 90 | line_plan plan = { 91 | .x = {100*400, 0, 0}, 92 | .feed = 20, 93 | .feed0 = 0, 94 | .feed1 = 0, 95 | .acceleration = 40, 96 | .len = -1, // must be negative at init 97 | }; 98 | 99 | pos[0] = pos[1] = pos[2] = 0; 100 | line_move_to(&plan); 101 | int delay = -1; 102 | do 103 | { 104 | delay = line_step_tick(); 105 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 106 | } while (delay > 0); 107 | } 108 | 109 | void test_4(void) 110 | { 111 | steppers_definition def = { 112 | .set_dir = set_dir, 113 | .make_step = make_step, 114 | .steps_per_unit = {400, 400, 400}, 115 | .feed_base = 0.01, 116 | }; 117 | 118 | moves_common_init(&def); 119 | 120 | line_plan plan = { 121 | .x = {100*400, 0, 0}, 122 | .feed = 100./60, 123 | .feed0 = 100./60, 124 | .feed1 = 100./60, 125 | .acceleration = 40.0, 126 | .len = -1, // must be negative at init 127 | }; 128 | 129 | pos[0] = pos[1] = pos[2] = 0; 130 | line_move_to(&plan); 131 | int delay = -1; 132 | int time = 0; 133 | do 134 | { 135 | delay = line_step_tick(); 136 | if (delay > 0) 137 | time += delay; 138 | printf("%i %i %i, %i\n", pos[0], pos[1], pos[2], delay); 139 | } while (delay > 0); 140 | 141 | printf("%i\n", time); 142 | } 143 | 144 | 145 | int main(void) 146 | { 147 | test_4(); 148 | return 0; 149 | } 150 | 151 | 152 | -------------------------------------------------------------------------------- /core/control/planner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(planner STATIC planner.c) 2 | target_include_directories(planner PUBLIC .) 3 | 4 | target_link_libraries(planner PUBLIC moves tools) 5 | -------------------------------------------------------------------------------- /core/control/planner/planner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int empty_slots(void); 12 | 13 | void init_planner(steppers_definition *pd, 14 | gpio_definition *gd, 15 | void (*arg_send_queued)(int nid), 16 | void (*arg_send_started)(int nid), 17 | void (*arg_send_completed)(int nid), 18 | void (*arg_send_completed_with_pos)(int nid, const int32_t *pos), 19 | void (*arg_send_dropped)(int nid), 20 | void (*arg_send_failed)(int nid)); 21 | 22 | int planner_line_to(int32_t x[3], double feed, double f0, double f1, int32_t acc, int nid); 23 | 24 | int planner_arc_to(int32_t x1[2], int32_t x2[2], int32_t H, double len, double a, double b, arc_plane plane, int cw, 25 | double feed, double f0, double f1, int32_t acc, int nid); 26 | 27 | int planner_tool(int id, bool on, int nid); 28 | 29 | void planner_pre_calculate(void); 30 | 31 | void enable_break_on_probe(bool en); 32 | 33 | void planner_unlock(void); 34 | 35 | void planner_lock(void); 36 | 37 | int planner_is_locked(void); 38 | 39 | void planner_fail_on_endstops(bool fail); 40 | 41 | void planner_report_states(void); 42 | 43 | -------------------------------------------------------------------------------- /core/control/system.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void (*rb)(void); 4 | 5 | void system_init(void (*reboot)(void)) 6 | { 7 | rb = reboot; 8 | } 9 | 10 | void system_reboot(void) 11 | { 12 | rb(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /core/control/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void system_init(void (*reboot)(void)); 4 | void system_reboot(void); 5 | 6 | -------------------------------------------------------------------------------- /core/control/tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tools STATIC tools.c) 2 | target_include_directories(tools PUBLIC .) 3 | 4 | target_link_libraries(tools PUBLIC err) 5 | -------------------------------------------------------------------------------- /core/control/tools/tools.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static gpio_definition *def; 5 | void tools_init(gpio_definition *definition) 6 | { 7 | def = definition; 8 | } 9 | 10 | int tool_action(tool_plan *plan) 11 | { 12 | def->set_gpio(plan->id, plan->on); 13 | return -E_NEXT; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /core/control/tools/tools.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct 6 | { 7 | void (*set_gpio)(int id, int state); 8 | } gpio_definition; 9 | 10 | typedef struct 11 | { 12 | bool on; 13 | int id; 14 | } tool_plan; 15 | 16 | void tools_init(gpio_definition *definition); 17 | int tool_action(tool_plan *plan); 18 | 19 | -------------------------------------------------------------------------------- /core/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "arch-defs.h" 4 | 5 | -------------------------------------------------------------------------------- /core/err/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(err INTERFACE) 2 | target_include_directories(err INTERFACE .) 3 | -------------------------------------------------------------------------------- /core/err/err.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { 4 | E_OK = 0, 5 | E_NULL = 1, 6 | E_BADNUM = 2, 7 | E_INCORRECT = 3, 8 | E_UNKNOWN = 4, 9 | E_NOMEM = 5, 10 | E_LOCKED = 6, 11 | E_CRC = 7, 12 | E_ENDSTOP = 8, 13 | E_NEXT = 9, 14 | E_WAIT = 10, 15 | }; 16 | -------------------------------------------------------------------------------- /core/gcode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(gcode STATIC gcodes.c) 2 | target_include_directories(gcode PUBLIC .) 3 | target_link_libraries(gcode err) 4 | -------------------------------------------------------------------------------- /core/gcode/gcodes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static int islast(unsigned char c) 7 | { 8 | return c == 0 || c == ';' || c == '\n'; 9 | } 10 | 11 | static int read_int(const unsigned char **str, const unsigned char *end, int32_t *val) 12 | { 13 | if (*str >= end) 14 | return -E_BADNUM; 15 | if ((**str >= '0' && **str <= '9') || **str == '-') { 16 | int32_t v = 0; 17 | int8_t minus = 1; 18 | if (**str == '-') { 19 | minus = -1; 20 | (*str)++; 21 | } 22 | if (*str >= end) 23 | return -E_BADNUM; 24 | while (**str >= '0' && **str <= '9' && *str < end) { 25 | v *= 10; 26 | v += (**str - '0'); 27 | (*str)++; 28 | } 29 | v *= minus; 30 | *val = v; 31 | return E_OK; 32 | } 33 | return -E_BADNUM; 34 | } 35 | 36 | static uint8_t hex_decode(unsigned char c) 37 | { 38 | if (c >= '0' && c <= '9') 39 | return c - '0'; 40 | if (c >= 'a' && c <= 'f') 41 | return c - 'a' + 10; 42 | if (c >= 'A' && c <= 'F') 43 | return c - 'A' + 10; 44 | return 0xFF; 45 | } 46 | 47 | static uint8_t is_hex(unsigned char c) 48 | { 49 | if (c >= '0' && c <= '9') 50 | return 1; 51 | if (c >= 'a' && c <= 'f') 52 | return 1; 53 | if (c >= 'A' && c <= 'F') 54 | return 1; 55 | return 0; 56 | } 57 | 58 | static int read_hex(const unsigned char **str, const unsigned char *end, int32_t *val) 59 | { 60 | if (*str >= end) 61 | return -E_BADNUM; 62 | if (is_hex(**str)) { 63 | int32_t v = 0; 64 | while (is_hex(**str) && *str < end) { 65 | v *= 16; 66 | v += hex_decode(**str); 67 | (*str)++; 68 | } 69 | *val = v; 70 | return E_OK; 71 | } 72 | return -E_BADNUM; 73 | } 74 | 75 | static int read_double(const unsigned char **str, const unsigned char *end, double *val) 76 | { 77 | if (*str >= end) 78 | return -E_BADNUM; 79 | 80 | if ((**str >= '0' && **str <= '9') || **str == '-' || **str == '.') 81 | { 82 | int8_t minus = 1; 83 | double v = 0; 84 | 85 | if (**str == '-') 86 | { 87 | minus = -1; 88 | (*str)++; 89 | } 90 | 91 | if (*str >= end) 92 | return -E_BADNUM; 93 | 94 | while (**str >= '0' && **str <= '9' && *str < end) 95 | { 96 | v *= 10; 97 | v += (**str - '0'); 98 | (*str)++; 99 | } 100 | 101 | if (**str == '.') 102 | { 103 | uint8_t s = 0; 104 | double div = 10; 105 | (*str)++; 106 | while (**str >= '0' && **str <= '9' && *str < end) 107 | { 108 | v += (**str - '0') / div; 109 | (*str)++; 110 | div *= 10; 111 | } 112 | } 113 | 114 | v *= minus; 115 | *val = v; 116 | return E_OK; 117 | } 118 | return -E_BADNUM; 119 | } 120 | 121 | static bool is_float(const unsigned char *str, const unsigned char *end) 122 | { 123 | while (str < end) 124 | { 125 | if (*str >= '0' && *str <= '9') 126 | { 127 | str++; 128 | continue; 129 | } 130 | if (*str == '.') 131 | return true; 132 | return false; 133 | } 134 | return false; 135 | } 136 | 137 | static int parse_element(const unsigned char **str, const unsigned char *end, gcode_cmd_t *cmd) 138 | { 139 | if (str == NULL || *str == NULL || *str >= end) 140 | return -E_NULL; 141 | 142 | while (**str == ' ') 143 | (*str)++; 144 | 145 | if (islast(**str) || *str >= end) { 146 | cmd->type = 0; 147 | return E_OK; 148 | } 149 | 150 | if (!(**str >= 'A' && **str <= 'Z')) 151 | return -E_INCORRECT; 152 | 153 | cmd->type = **str; 154 | (*str)++; 155 | if (is_float(*str, end)) 156 | { 157 | if (read_double(str, end, &(cmd->val_f))) 158 | return -E_BADNUM; 159 | } 160 | else 161 | { 162 | if (read_int(str, end, &(cmd->val_i))) 163 | return -E_BADNUM; 164 | } 165 | 166 | return E_OK; 167 | } 168 | 169 | int parse_cmdline(const unsigned char *str, size_t len, gcode_frame_t *frame) 170 | { 171 | int i = 0, rc; 172 | const unsigned char *str0 = str, *end = str + len; 173 | int finish = 0; 174 | while (*str != 0 && i < MAX_CMDS && !finish && str < end) 175 | { 176 | if ((rc = parse_element(&str, end, &(frame->cmds[i]))) < 0) 177 | { 178 | return rc; 179 | } 180 | switch (frame->cmds[i].type) 181 | { 182 | case '*': 183 | { 184 | int sum = 0; 185 | int crc = frame->cmds[i].val_i; 186 | while (*str0 != '*') 187 | { 188 | sum += (*str0); 189 | str0++; 190 | } 191 | if (sum != crc) 192 | { 193 | return -E_CRC; 194 | } 195 | finish = 1; 196 | break; 197 | } 198 | case 0: 199 | { 200 | finish = 1; 201 | break; 202 | } 203 | default: 204 | { 205 | i++; 206 | break; 207 | } 208 | } 209 | } 210 | frame->num = i; 211 | return -E_OK; 212 | } 213 | 214 | -------------------------------------------------------------------------------- /core/gcode/gcodes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_CMDS 16 8 | 9 | typedef struct { 10 | char type; 11 | union { 12 | int32_t val_i; 13 | double val_f; 14 | }; 15 | } gcode_cmd_t; 16 | 17 | typedef struct { 18 | int num; 19 | gcode_cmd_t cmds[MAX_CMDS]; 20 | } gcode_frame_t; 21 | 22 | int parse_cmdline(const unsigned char *str, size_t len, gcode_frame_t *frame); 23 | -------------------------------------------------------------------------------- /core/output/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(output STATIC output.c) 2 | target_include_directories(output PUBLIC .) 3 | -------------------------------------------------------------------------------- /core/output/output.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static int control_fd; 6 | static int shell_fd; 7 | static ssize_t (*write_fun)(int, const void *, ssize_t); 8 | 9 | void output_control_set_fd(int fd) 10 | { 11 | control_fd = fd; 12 | } 13 | 14 | void output_shell_set_fd(int fd) 15 | { 16 | shell_fd = fd; 17 | } 18 | 19 | void output_set_write_fun(ssize_t (*write_f)(int, const void *, ssize_t)) 20 | { 21 | write_fun = write_f; 22 | } 23 | 24 | void output_control_write(const char *buf, ssize_t len) 25 | { 26 | if (len < 0) 27 | len = strlen(buf); 28 | write_fun(control_fd, buf, len); 29 | } 30 | 31 | void output_shell_write(const char *buf, ssize_t len) 32 | { 33 | if (len < 0) 34 | len = strlen(buf); 35 | write_fun(shell_fd, buf, len); 36 | } 37 | -------------------------------------------------------------------------------- /core/output/output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | void output_control_set_fd(int fd); 6 | void output_shell_set_fd(int fd); 7 | 8 | void output_set_write_fun(ssize_t (*write_f)(int, const void *, ssize_t)); 9 | 10 | void output_control_write(const char *buf, ssize_t len); 11 | void output_shell_write(const char *buf, ssize_t len); 12 | -------------------------------------------------------------------------------- /core/unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_gcode test_gcode.c) 2 | target_link_libraries(test_gcode gcode control) 3 | 4 | add_executable(test_lines test_lines.c) 5 | target_link_libraries(test_lines control) 6 | 7 | add_executable(test_planner test_planner.c) 8 | target_link_libraries(test_planner control) 9 | -------------------------------------------------------------------------------- /core/unit_tests/test_gcode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void test_G0(void) 6 | { 7 | printf("Test G0: "); 8 | gcode_frame_t frame; 9 | const char gcode[] = "G0X10Y10F100P0L0"; 10 | int res = parse_cmdline(gcode, &frame); 11 | assert(res == -E_OK); 12 | assert(frame.num == 6); 13 | printf("ok\n"); 14 | } 15 | 16 | void test_G28(void) 17 | { 18 | printf("Test G28: "); 19 | gcode_frame_t frame; 20 | const char gcode[] = "G0X"; 21 | int res = parse_cmdline(gcode, &frame); 22 | assert(res == -E_OK); 23 | assert(frame.num == 2); 24 | assert(frame.cmds[1].type == 'X'); 25 | assert(frame.cmds[1].val_i == 0); 26 | printf("ok\n"); 27 | } 28 | 29 | int main(void) 30 | { 31 | test_G0(); 32 | test_G28(); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /core/unit_tests/test_lines.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define ACC 50 7 | #define STEPS_PER_MM 400 8 | #define FEED_BASE 5 9 | #define FEED_MAX 1500 10 | 11 | #define SIZE_X 1000 12 | #define SIZE_Y 1000 13 | #define SIZE_Z 1000 14 | 15 | static volatile int moving = 0; 16 | 17 | static void line_started(void) 18 | { 19 | printf("Line started\n"); 20 | moving = 1; 21 | } 22 | 23 | 24 | static void line_finished(void) 25 | { 26 | printf("Line finished\n"); 27 | moving = 0; 28 | } 29 | 30 | static void line_error(void) 31 | { 32 | printf("Line error\n"); 33 | moving = 0; 34 | } 35 | 36 | static int s[3], d[3] = {1, 1, 1}; 37 | 38 | static cnc_endstops get_stops(void) 39 | { 40 | cnc_endstops stops = { 41 | .stop_x = s[0] <= 0, 42 | .stop_y = s[1] <= 0, 43 | .stop_z = s[2] <= 0, 44 | .probe = 0, 45 | }; 46 | 47 | return stops; 48 | } 49 | 50 | static void make_step(int i) 51 | { 52 | s[i] += d[i]; 53 | } 54 | 55 | static void set_dir(int i, bool dir) 56 | { 57 | if (dir) 58 | d[i] = 1; 59 | else 60 | d[i] = -1; 61 | } 62 | 63 | static void init_lines(void) 64 | { 65 | static steppers_definition sd = { 66 | .set_dir = set_dir, 67 | .make_step = make_step, 68 | .get_endstops = get_stops, 69 | .line_started = line_started, 70 | .line_finished = line_finished, 71 | .line_error = line_error, 72 | .steps_per_unit = { 73 | STEPS_PER_MM, 74 | STEPS_PER_MM, 75 | STEPS_PER_MM 76 | }, 77 | .feed_base = FEED_BASE, 78 | .feed_max = FEED_MAX, 79 | .size = { 80 | SIZE_X, 81 | SIZE_Y, 82 | SIZE_Z, 83 | }, 84 | .acc_default = ACC, 85 | }; 86 | 87 | moves_common_init(&sd); 88 | } 89 | 90 | void test_line_x(void) 91 | { 92 | int i; 93 | printf("\ntest_line_x\n"); 94 | line_plan plan = { 95 | .x = { 96 | 10*STEPS_PER_MM, 97 | 0, 98 | 0 99 | }, 100 | .feed = 100, 101 | .feed0 = 0, 102 | .feed1 = 0, 103 | .acceleration = ACC, 104 | }; 105 | 106 | init_lines(); 107 | line_pre_calculate(&plan); 108 | 109 | for (i = 0; i < 3; i++) 110 | s[i] = 0; 111 | int res = line_move_to(&plan); 112 | assert(res == -E_OK); 113 | 114 | assert(moving == 1); 115 | while (moving) 116 | line_step_tick(); 117 | for (i = 0; i < 3; i++) 118 | assert(s[i] == plan.x[i]); 119 | } 120 | 121 | void test_line_xyz(void) 122 | { 123 | int i; 124 | printf("\ntest_line_xyz\n"); 125 | line_plan plan = { 126 | .x = { 127 | 10*STEPS_PER_MM, 128 | 1*STEPS_PER_MM, 129 | 1*STEPS_PER_MM 130 | }, 131 | .feed = 100, 132 | .feed0 = 0, 133 | .feed1 = 0, 134 | .acceleration = ACC, 135 | }; 136 | 137 | init_lines(); 138 | line_pre_calculate(&plan); 139 | 140 | for (i = 0; i < 3; i++) 141 | s[i] = 0; 142 | int res = line_move_to(&plan); 143 | assert(res == -E_OK); 144 | assert(moving == 1); 145 | while (moving) 146 | line_step_tick(); 147 | for (i = 0; i < 3; i++) 148 | assert(s[i] == plan.x[i]); 149 | } 150 | 151 | void test_line_empty(void) 152 | { 153 | int i; 154 | printf("\ntest_line_next\n"); 155 | line_plan plan = { 156 | .x = { 157 | 0, 158 | 0, 159 | 0 160 | }, 161 | .feed = 100, 162 | .feed0 = 0, 163 | .feed1 = 0, 164 | .acceleration = ACC, 165 | }; 166 | 167 | init_lines(); 168 | line_pre_calculate(&plan); 169 | 170 | int res = line_move_to(&plan); 171 | assert(res == -E_NEXT); 172 | } 173 | 174 | void test_multiple_lines(void) 175 | { 176 | int i; 177 | printf("\ntest_multiple_lines\n"); 178 | line_plan plan1 = { 179 | .x = { 180 | 4000, 181 | 0, 182 | 0 183 | }, 184 | .feed = 15, 185 | .feed0 = 0, 186 | .feed1 = 0, 187 | .acceleration = 40, 188 | }; 189 | 190 | init_lines(); 191 | line_pre_calculate(&plan1); 192 | 193 | for (i = 0; i < 3; i++) 194 | s[i] = 0; 195 | 196 | int res = line_move_to(&plan1); 197 | assert(res == -E_OK); 198 | assert(moving == 1); 199 | while (moving) 200 | line_step_tick(); 201 | 202 | printf("pos1 = %i %i %i\n", s[0], s[1], s[2]); 203 | for (i = 0; i < 3; i++) 204 | { 205 | assert(s[i] == plan1.x[i]); 206 | } 207 | 208 | line_plan plan2 = { 209 | .x = { 210 | -4000, 211 | 0, 212 | 0 213 | }, 214 | .feed = 15, 215 | .feed0 = 0, 216 | .feed1 = 0, 217 | .acceleration = 40, 218 | }; 219 | 220 | line_pre_calculate(&plan2); 221 | 222 | res = line_move_to(&plan2); 223 | assert(res == -E_OK); 224 | assert(moving == 1); 225 | while (moving) 226 | line_step_tick(); 227 | 228 | printf("pos2 = %i %i %i\n", s[0], s[1], s[2]); 229 | for (i = 0; i < 3; i++) 230 | { 231 | assert(s[i] == plan1.x[i] + plan2.x[i]); 232 | } 233 | 234 | line_plan plan3 = { 235 | .x = { 236 | 4000, 237 | 0, 238 | 0 239 | }, 240 | .feed = 15, 241 | .feed0 = 0, 242 | .feed1 = 0, 243 | .acceleration = 40, 244 | }; 245 | 246 | line_pre_calculate(&plan3); 247 | 248 | res = line_move_to(&plan3); 249 | assert(res == -E_OK); 250 | assert(moving == 1); 251 | while (moving) 252 | line_step_tick(); 253 | 254 | printf("pos3 = %i %i %i\n", s[0], s[1], s[2]); 255 | for (i = 0; i < 3; i++) 256 | { 257 | assert(s[i] == plan1.x[i] + plan2.x[i] + plan3.x[i]); 258 | } 259 | 260 | } 261 | 262 | int main(void) 263 | { 264 | /* test_line_x(); 265 | test_line_xyz(); 266 | test_line_empty();*/ 267 | test_multiple_lines(); 268 | return 0; 269 | } 270 | 271 | -------------------------------------------------------------------------------- /core/unit_tests/test_planner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define ACC 50 8 | #define STEPS_PER_MM 400 9 | #define FEED_BASE 5 10 | #define FEED_MAX 1500 11 | 12 | #define SIZE_X 1000 13 | #define SIZE_Y 1000 14 | #define SIZE_Z 1000 15 | 16 | static volatile int moving = 0; 17 | 18 | static void line_started(void) 19 | { 20 | printf("Line started\n"); 21 | moving = 1; 22 | } 23 | 24 | 25 | static void line_finished(void) 26 | { 27 | printf("Line finished\n"); 28 | moving = 0; 29 | } 30 | 31 | static void line_error(void) 32 | { 33 | printf("Line error\n"); 34 | moving = 0; 35 | } 36 | 37 | static int s[3], d[3] = {1, 1, 1}; 38 | 39 | static cnc_endstops get_stops(void) 40 | { 41 | cnc_endstops stops = { 42 | .stop_x = s[0] <= 0, 43 | .stop_y = s[1] <= 0, 44 | .stop_z = s[2] <= 0, 45 | .probe = 0, 46 | }; 47 | 48 | return stops; 49 | } 50 | 51 | static void make_step(int i) 52 | { 53 | s[i] += d[i]; 54 | } 55 | 56 | static void set_dir(int i, bool dir) 57 | { 58 | if (dir) 59 | d[i] = 1; 60 | else 61 | d[i] = -1; 62 | } 63 | 64 | static void send_queued(int nid) 65 | { 66 | printf("%i queued\n", nid); 67 | } 68 | 69 | static void send_started(int nid) 70 | { 71 | printf("%i started\n", nid); 72 | } 73 | 74 | static void send_completed(int nid) 75 | { 76 | printf("%i completed\n", nid); 77 | } 78 | 79 | static void send_completed_with_pos(int nid, const int *pos) 80 | { 81 | printf("%i completed. X=%i Y=%i Z=%i\n", nid, pos[0], pos[1], pos[2]); 82 | } 83 | 84 | static void send_dropped(int nid) 85 | { 86 | printf("%i dropped\n", nid); 87 | } 88 | 89 | static void send_failed(int nid) 90 | { 91 | printf("%i failed\n", nid); 92 | } 93 | 94 | static void init(void) 95 | { 96 | static steppers_definition sd = { 97 | .set_dir = set_dir, 98 | .make_step = make_step, 99 | .get_endstops = get_stops, 100 | .line_started = line_started, 101 | .line_finished = line_finished, 102 | .line_error = line_error, 103 | .steps_per_unit = { 104 | STEPS_PER_MM, 105 | STEPS_PER_MM, 106 | STEPS_PER_MM 107 | }, 108 | .feed_base = FEED_BASE, 109 | .feed_max = FEED_MAX, 110 | .size = { 111 | SIZE_X, 112 | SIZE_Y, 113 | SIZE_Z, 114 | }, 115 | .acc_default = ACC, 116 | }; 117 | 118 | static gpio_definition gd = { 119 | }; 120 | 121 | init_planner(&sd, &gd, send_queued, send_started, send_completed, send_completed_with_pos, send_dropped, send_failed); 122 | } 123 | 124 | void test_line(void) 125 | { 126 | int i; 127 | printf("\ntest_line\n"); 128 | 129 | s[0] = s[1] = s[2] = 0; 130 | 131 | init(); 132 | int32_t x[3] = {4000, 0, 0}; 133 | planner_unlock(); 134 | planner_line_to(x, 15, 0, 0, 40, 1); 135 | 136 | assert(moving == 1); 137 | 138 | while(moving) 139 | { 140 | moves_step_tick(); 141 | } 142 | 143 | assert(s[0] == x[0]); 144 | assert(s[1] == x[1]); 145 | assert(s[2] == x[2]); 146 | } 147 | 148 | void test_multiple_lines(void) 149 | { 150 | int i; 151 | printf("\ntest_multiple_lines\n"); 152 | 153 | s[0] = s[1] = s[2] = 0; 154 | 155 | init(); 156 | int32_t x1[3] = {4000, 0, 0}; 157 | int32_t x2[3] = {-4000, 0, 0}; 158 | int32_t x3[3] = {4000, 0, 0}; 159 | planner_unlock(); 160 | planner_line_to(x1, 15, 0, 0, 40, 1); 161 | planner_line_to(x2, 15, 0, 0, 40, 2); 162 | planner_line_to(x3, 15, 0, 0, 40, 3); 163 | 164 | assert(moving == 1); 165 | 166 | while(moving) 167 | { 168 | moves_step_tick(); 169 | } 170 | 171 | for (i = 0; i < 3; i++) 172 | assert(s[i] == x1[i] + x2[i] + x3[i]); 173 | } 174 | 175 | int main(void) 176 | { 177 | test_multiple_lines(); 178 | 179 | return 0; 180 | } 181 | 182 | -------------------------------------------------------------------------------- /debugutils/sender.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time 4 | import getopt 5 | import sys 6 | import serial 7 | import re 8 | 9 | baudrate = 57600 10 | port = "/dev/ttyUSB0" 11 | f = None 12 | 13 | def usage(): 14 | print("Usage: sender.py [-h] [-b baudrate] [-p serial port] -f gcode_file") 15 | 16 | try: 17 | optlist, args = getopt.getopt(sys.argv[1:], "hf:p:b:") 18 | except getopt.GetoptError as err: 19 | usage() 20 | sys.exit(1) 21 | 22 | for o, a in optlist: 23 | if o == "-h": 24 | usage() 25 | sys.exit(0) 26 | elif o == "-f": 27 | f = a 28 | elif o == "-b": 29 | baudrate = int(a) 30 | elif o == "-p": 31 | port = a 32 | else: 33 | usage() 34 | sys.exit(1) 35 | if f == None: 36 | usage() 37 | sys.exit(1) 38 | 39 | ser = serial.Serial(port, baudrate, bytesize=8, parity='N', stopbits=1, timeout=1) 40 | 41 | source = open(f, "r") 42 | gcoderaw = source.readlines() 43 | source.close() 44 | 45 | gcode = [] 46 | 47 | for gc in gcoderaw: 48 | gc = gc.strip("\n") 49 | gc = gc.strip("\r") 50 | if len(gc) > 0: 51 | gcode.append(gc) 52 | 53 | for gc in gcode: 54 | print("Sending: %s" % gc) 55 | cmd = gc + "\n" 56 | ser.write(bytes(cmd, "UTF-8")) 57 | ser.flush() 58 | ans = "" 59 | while ans == "": 60 | ans = ser.readline().decode("UTF-8") 61 | print("Ans: %s" % ans) 62 | #time.sleep(0.5) 63 | 64 | ser.close() 65 | 66 | -------------------------------------------------------------------------------- /drivers/Makefile: -------------------------------------------------------------------------------- 1 | DRIVERS := 2 | 3 | all: drvs 4 | 5 | ifdef CONFIG_ETHERNET_DEVICE_ENC28J60 6 | DRIVERS += build_ethernet_enc28j60 7 | 8 | build_ethernet_enc28j60: 9 | $(MAKE) -C ethernet_enc28j60/ 10 | endif 11 | 12 | drvs: $(DRIVERS) 13 | 14 | clean: 15 | $(MAKE) -C ethernet_enc28j60/ clean 16 | 17 | -------------------------------------------------------------------------------- /include/config/auto.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Main menu 4 | # 5 | -------------------------------------------------------------------------------- /include/config/auto.conf.cmd: -------------------------------------------------------------------------------- 1 | deps_config := \ 2 | Kconfig 3 | 4 | include/config/auto.conf: \ 5 | $(deps_config) 6 | 7 | 8 | $(deps_config): ; 9 | -------------------------------------------------------------------------------- /include/generated/autoconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Automatically generated file; DO NOT EDIT. 4 | * Main menu 5 | * 6 | */ 7 | -------------------------------------------------------------------------------- /libmodbus/Makefile: -------------------------------------------------------------------------------- 1 | SRCS := modbus.c 2 | 3 | OBJS := $(SRCS:%.c=%.o) 4 | SUS := $(SRCS:%.c=%.su) 5 | 6 | PWD := $(shell pwd) 7 | CC += -I$(PWD) 8 | 9 | all: libmodbus.a 10 | 11 | libmodbus.a: $(OBJS) 12 | $(AR) rsc $@ $^ 13 | 14 | %.o: %.c 15 | $(CC) $< -c -o $@ 16 | 17 | clean: 18 | rm -f $(OBJS) libmodbus.a $(SUS) 19 | 20 | -------------------------------------------------------------------------------- /libmodbus/modbus.c: -------------------------------------------------------------------------------- 1 | #include "modbus.h" 2 | #include 3 | 4 | ssize_t modbus_fill_write_ao(uint8_t *buf, uint16_t reg, uint16_t val) 5 | { 6 | struct modbus_write_ao_s *wr = (struct modbus_write_ao_s *)(buf); 7 | wr->reg_h = (uint8_t)(reg >> 8); 8 | wr->reg_l = (uint8_t)(reg); 9 | 10 | wr->val_h = (uint8_t)(val >> 8); 11 | wr->val_l = (uint8_t)(val); 12 | 13 | return MODBUS_WRITE_AO_LEN; 14 | } 15 | 16 | uint16_t crc16(const uint8_t *buf, int len) 17 | { 18 | static const uint16_t crcTable[] = { 19 | 0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, 20 | 0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, 21 | 0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, 22 | 0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, 23 | 0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, 24 | 0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, 25 | 0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, 26 | 0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, 27 | 0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, 28 | 0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, 29 | 0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, 30 | 0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, 31 | 0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, 32 | 0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, 33 | 0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, 34 | 0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, 35 | 0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, 36 | 0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, 37 | 0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, 38 | 0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, 39 | 0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, 40 | 0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, 41 | 0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, 42 | 0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, 43 | 0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, 44 | 0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, 45 | 0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, 46 | 0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, 47 | 0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, 48 | 0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, 49 | 0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, 50 | 0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 51 | }; 52 | 53 | uint8_t temp; 54 | uint16_t CRC = 0xFFFF; 55 | 56 | while (len--) 57 | { 58 | temp = *(buf++) ^ CRC; 59 | CRC >>= 8; 60 | CRC ^= crcTable[temp]; 61 | } 62 | return CRC; 63 | } 64 | 65 | ssize_t modbus_fill_header(uint8_t *buf, uint8_t address, uint8_t function, size_t len) 66 | { 67 | if (len > 252) 68 | return -1; 69 | 70 | struct modbus_header_s *hdr = (struct modbus_header_s *)buf; 71 | 72 | hdr->address = address; 73 | hdr->function = function; 74 | 75 | uint8_t *crc_l = buf + MODBUS_HEADER_LEN + len; 76 | uint8_t *crc_h = buf + MODBUS_HEADER_LEN + len+1; 77 | uint16_t crc = crc16(buf, MODBUS_HEADER_LEN + len); 78 | 79 | *crc_l = (uint8_t)crc; 80 | *crc_h = (uint8_t)(crc >> 8); 81 | 82 | return MODBUS_HEADER_LEN + len + 2; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /libmodbus/modbus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "arch-defs.h" 9 | 10 | struct modbus_header_s 11 | { 12 | uint8_t address; 13 | uint8_t function; 14 | }; 15 | 16 | struct modbus_write_ao_s 17 | { 18 | uint16_t reg_h : 8; 19 | uint16_t reg_l : 8; 20 | 21 | uint16_t val_h : 8; 22 | uint16_t val_l : 8; 23 | }; 24 | 25 | #define MODBUS_HEADER_LEN sizeof(struct modbus_header_s) 26 | #define MODBUS_WRITE_AO_LEN sizeof(struct modbus_write_ao_s) 27 | 28 | #define FUNCTION_READ_DO 0x01 29 | #define FUNCTION_READ_DI 0x02 30 | #define FUNCTION_READ_AO 0x03 31 | #define FUNCTION_READ_AI 0x04 32 | #define FUNCTION_WRITE_DO 0x05 33 | #define FUNCTION_WRITE_AO 0x06 34 | #define FUNCTION_WRITE_MULTIPLE_DO 0x0F 35 | #define FUNCTION_WRITE_MULTIPLE_AO 0x10 36 | 37 | ssize_t modbus_fill_write_ao(uint8_t *buf, uint16_t reg, uint16_t val); 38 | ssize_t modbus_fill_header(uint8_t *buf, uint8_t address, uint8_t function, size_t datalen); 39 | 40 | -------------------------------------------------------------------------------- /libs/Makefile: -------------------------------------------------------------------------------- 1 | LIBS := 2 | 3 | all: libs 4 | 5 | LIBS += build_libshell 6 | build_libshell: 7 | $(MAKE) -C libshell/ 8 | 9 | ifdef CONFIG_IP 10 | LIBS += build_libip 11 | build_libip: 12 | $(MAKE) -C libip/ 13 | endif 14 | 15 | libs: $(LIBS) 16 | @echo "LIBS = $(LIBS)" 17 | 18 | clean: 19 | $(MAKE) -C libip/ clean 20 | $(MAKE) -C libshell/ clean 21 | -------------------------------------------------------------------------------- /libs/libshell/Makefile: -------------------------------------------------------------------------------- 1 | FREERTOS_PATH := $(ROOT)/libs/FreeRTOS-Kernel 2 | CC += -I$(FREERTOS_PATH)/include 3 | CC += -I$(FREERTOS_PATH)/portable/GCC/ARM_CM4F 4 | 5 | SRCS := shell.c 6 | 7 | OBJS := $(SRCS:%.c=%.o) 8 | SUS := $(SRCS:%.c=%.su) 9 | 10 | PWD := $(shell pwd) 11 | CC += -I$(PWD) 12 | 13 | ifdef CONFIG_LIBCORE 14 | CC += -I$(ROOT)/core/ -DCONFIG_LIBCORE 15 | endif 16 | 17 | ifdef CONFIG_LIBMODBUS 18 | CC += -I$(ROOT)/libmodbus/ -DCONFIG_LIBMODBUS 19 | endif 20 | 21 | ifdef CONFIG_ECHO 22 | CC += -DCONFIG_ECHO 23 | endif 24 | 25 | ifdef CONFIG_COPY_COMMAND 26 | CC += -DCONFIG_COPY_COMMAND 27 | endif 28 | 29 | 30 | CC += -DSHELL_RING_LEN=$(CONFIG_SHELL_RING_LEN) 31 | CC += -DSHELL_MSG_LEN=$(CONFIG_SHELL_MSG_LEN) 32 | 33 | all: libshell.a 34 | 35 | libshell.a: $(OBJS) 36 | $(AR) rsc $@ $^ 37 | 38 | %.o: %.c 39 | $(CC) $< -c -o $@ 40 | 41 | clean: 42 | rm -f $(OBJS) libshell.a $(SUS) 43 | 44 | -------------------------------------------------------------------------------- /libs/libshell/shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #ifdef CONFIG_LIBCORE 8 | #include 9 | #include 10 | #endif 11 | 12 | #ifdef CONFIG_LIBMODBUS 13 | #include 14 | #endif 15 | 16 | uint32_t shell_fails = 0; 17 | 18 | static char messages[SHELL_RING_LEN][SHELL_MSG_LEN]; 19 | static int mpos = 0; 20 | static int mfirst = 0; 21 | static int mnum = 0; 22 | 23 | static char input_buffer[SHELL_MSG_LEN]; 24 | 25 | #ifdef CONFIG_COPY_COMMAND 26 | static char command_buffer[SHELL_MSG_LEN-3]; 27 | #endif 28 | static int input_pos = 0; 29 | 30 | static void (*debug_send)(const uint8_t *data, ssize_t len); 31 | static void (*uart_send)(const uint8_t *data, size_t len); 32 | 33 | static void shell_pop_message(void) 34 | { 35 | if (mnum == 0) 36 | return; 37 | mnum--; 38 | mfirst = (mfirst + 1) % SHELL_RING_LEN; 39 | } 40 | 41 | bool shell_add_message(const char *msg, ssize_t len) 42 | { 43 | if (mnum == SHELL_RING_LEN) 44 | { 45 | shell_fails++; 46 | return false; 47 | } 48 | if (len < 0) 49 | len = strlen(msg); 50 | 51 | len = len < SHELL_MSG_LEN - 2 ? len : SHELL_MSG_LEN - 2; 52 | memset(messages[mpos], 0, SHELL_MSG_LEN); 53 | messages[mpos][0] = len >> 8; 54 | messages[mpos][1] = len; 55 | strncpy(messages[mpos]+2, msg, len); 56 | mpos = (mpos + 1) % SHELL_RING_LEN; 57 | mnum++; 58 | return true; 59 | } 60 | 61 | static bool shell_has_pending_messages(void) 62 | { 63 | return mnum > 0; 64 | } 65 | 66 | static ssize_t write_fun(int fd, const void *data, ssize_t len) 67 | { 68 | int i; 69 | if (len < 0) 70 | len = strlen((const char *)data); 71 | if (fd == 0) 72 | { 73 | if (!shell_add_message(data, len)) 74 | return -1; 75 | } 76 | else 77 | { 78 | if (debug_send) 79 | debug_send(data, len); 80 | } 81 | return 0; 82 | } 83 | 84 | /* Public methods */ 85 | 86 | void shell_send_completed(void) 87 | { 88 | shell_pop_message(); 89 | } 90 | 91 | const uint8_t *shell_pick_message(ssize_t *len) 92 | { 93 | if (mnum > 0) 94 | { 95 | *len = ((int)messages[mfirst][0]) << 8 | messages[mfirst][1]; 96 | return messages[mfirst] + 2; 97 | } 98 | *len = -1; 99 | return NULL; 100 | } 101 | 102 | int shell_empty_slots(void) 103 | { 104 | return SHELL_RING_LEN - mnum; 105 | } 106 | 107 | static int hex2dig(char c) 108 | { 109 | if (c >= '0' && c <= '9') 110 | return c - '0'; 111 | if (c >= 'A' && c <= 'F') 112 | return c - 'A' + 0xA; 113 | if (c >= 'a' && c <= 'f') 114 | return c - 'a' + 0xA; 115 | return -1; 116 | } 117 | 118 | bool shell_data_received(const char *data, ssize_t len) 119 | { 120 | int i; 121 | if (len < 0) 122 | len = strlen(data); 123 | 124 | if (input_pos + len > SHELL_MSG_LEN) 125 | { 126 | input_pos = 0; 127 | return false; 128 | } 129 | memcpy(input_buffer + input_pos, data, len); 130 | for (i = 0; i < len; i++) 131 | { 132 | if (input_buffer[i + input_pos] == '\n' || input_buffer[i + input_pos] == '\r') 133 | input_buffer[i + input_pos] = ' '; 134 | } 135 | 136 | input_pos += len; 137 | return true; 138 | } 139 | 140 | void shell_data_completed(void) 141 | { 142 | if (input_pos >= 6 && !memcmp(input_buffer, "START:", 6)) 143 | { 144 | // Do nothing 145 | } 146 | #ifdef CONFIG_LIBCORE 147 | else if (input_pos >= 3 && !memcmp(input_buffer, "RT:", 3)) 148 | { 149 | #ifdef CONFIG_COPY_COMMAND 150 | memcpy(command_buffer, input_buffer+3, input_pos-3); 151 | execute_g_command(command_buffer, input_pos - 3); 152 | #else 153 | execute_g_command(input_buffer+3, input_pos - 3); 154 | #endif 155 | } 156 | #endif 157 | 158 | #ifdef CONFIG_LIBMODBUS 159 | else if (input_pos >= 3 && !memcmp(input_buffer, "MB:", 3)) 160 | { 161 | int i; 162 | const char *buf = input_buffer + 3; 163 | uint8_t addrs[4], vals[4], devids[4]; 164 | memcpy(devids, buf, 4); 165 | memcpy(addrs, buf+4+1, 4); 166 | memcpy(vals, buf+4+1+4+1, 4); 167 | for (i = 0; i < 4; i++) 168 | { 169 | devids[i] = hex2dig(devids[i]); 170 | addrs[i] = hex2dig(addrs[i]); 171 | vals[i] = hex2dig(vals[i]); 172 | } 173 | uint16_t addr = addrs[0] * 0x1000 + addrs[1] * 0x100 + addrs[2] * 0x10 + addrs[3]; 174 | uint16_t val = vals[0] * 0x1000 + vals[1] * 0x100 + vals[2] * 0x10 + vals[3]; 175 | uint16_t devid = devids[0] * 0x1000 + devids[1] * 0x100 + devids[2] * 0x10 + devids[3]; 176 | 177 | #define PREAMBLE 0 178 | uint8_t buffer[40]; 179 | memset(buffer, 0, PREAMBLE); 180 | ssize_t wrlen = modbus_fill_write_ao(buffer + PREAMBLE + MODBUS_HEADER_LEN, addr, val); 181 | ssize_t mblen = modbus_fill_header(buffer + PREAMBLE, devid, FUNCTION_WRITE_AO, wrlen); 182 | memset(buffer + PREAMBLE + mblen, 0, PREAMBLE); 183 | 184 | /* send to UART */ 185 | if (uart_send) 186 | uart_send(buffer, mblen + 2 * PREAMBLE); 187 | #undef PREAMBLE 188 | } 189 | #endif 190 | else if (input_pos >= 5 && !memcmp(input_buffer, "EXIT:", 5)) 191 | { 192 | // Do nothing 193 | } 194 | #ifdef CONFIG_ECHO 195 | else if (input_pos >= 5 && !memcmp(input_buffer, "ECHO:", 5)) 196 | { 197 | // Send echo back 198 | shell_add_message(input_buffer, input_pos); 199 | } 200 | #endif 201 | else 202 | { 203 | char buf[SHELL_MSG_LEN-2]; 204 | int l = snprintf(buf, 100, "Unknown command: %i [%.*s]", input_pos, input_pos, input_buffer); 205 | shell_add_message(buf, l); 206 | } 207 | 208 | input_pos = 0; 209 | } 210 | 211 | void shell_setup(void (*debug_send_fun)(const uint8_t *, ssize_t), void (*uart_send_fun)(const uint8_t *data, size_t len)) 212 | { 213 | uart_send = uart_send_fun; 214 | debug_send = debug_send_fun; 215 | #ifdef CONFIG_LIBCORE 216 | output_set_write_fun(write_fun); 217 | output_control_set_fd(0); 218 | output_shell_set_fd(1); 219 | #endif 220 | } 221 | 222 | -------------------------------------------------------------------------------- /libs/libshell/shell.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "arch-defs.h" 8 | 9 | 10 | extern uint32_t shell_fails; 11 | 12 | void shell_setup(void (*debug_send_fun)(const uint8_t *, ssize_t), void (*uart_send_fun)(const uint8_t *data, size_t len)); 13 | 14 | /* Output methods */ 15 | void shell_send_completed(void); 16 | const uint8_t *shell_pick_message(ssize_t *len); 17 | int shell_empty_slots(void); 18 | bool shell_add_message(const char *msg, ssize_t len); 19 | 20 | /* Input methods */ 21 | bool shell_data_received(const char *data, ssize_t len); 22 | void shell_data_completed(void); 23 | -------------------------------------------------------------------------------- /main/Makefile: -------------------------------------------------------------------------------- 1 | obj-y := main.o 2 | 3 | 4 | all: main.o 5 | -------------------------------------------------------------------------------- /util/echo-uart.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import sys 3 | 4 | N = 60 5 | baud = 38400 6 | port = sys.argv[1] 7 | 8 | ser = serial.Serial(port, baud) 9 | 10 | ser.write(b'START:\n') 11 | rx = ser.readline() 12 | print('RX: ', rx.strip()) 13 | 14 | 15 | for i in range(N): 16 | msg = "ECHO: " + "".join(["**"]*i) + "\n" 17 | tx = bytes(msg, 'utf8') 18 | print('TX: ', tx.strip()) 19 | ser.write(tx) 20 | rx = ser.readline() 21 | print('RX: ', rx.strip()) 22 | 23 | ser.close() 24 | 25 | --------------------------------------------------------------------------------