This project involves a lot of teaching an old company some new tricks, but there are a million things that could go wrong with this stunt! My goal was to keep costs low as we flush out a new process for doing things. Toaster Oven re-flow is just what it sounds like. Instead to spending $10K on a re-flow oven I did some reading and convinced my boss, Larry Skutchan, that we should at least try this. My yield stands at about 80%. We have prototyped this whole product in-house, the only thing we sent out were the board spins.
Category Archives: BrailleBuzz
This highlights some of my work at American Printing House for the Blind.
The Prototype
Here is the prototype fresh off the 3D printer! It was a real pleasure to work with Industrial Designer / Modeler Andrew DakinĀ and Industrial Engineer / 3D printer guru Andrew Moulton, they took some very primitive ideas and turned them into something awesome! It’s hard to see but the 26 keys across the top have the Braille letters of the alphabet on them.
Braille Buzz Main Board
This is the second board spin. I designed this board and product from scratch. I started with the data-sheets for the ST Micro chip, took some tips form the Olmex boards that featured a similar chip, crossed my fingers and it worked! I have used Eagle (very popular), and Altium (very complicated), and I even tried PCB (too primitive) but my favorite is kiCad (just right). I suspect Verilog can be used for PCB layout, it’s really for “programming” logic gates, but I have my suspicions that it could be a good choice for really complex PCBs.
OpenOCD for BrailleBuzz (STM32F103)
This is my convenience stuff for working with openOCD. “debug” loads the “executable” bin and halts the MCU. “run” loads the “executable” bin and starts it running. “samples” loads some data in to the second bank of flash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
source [find interface/ftdi/olimex-arm-usb-ocd-h.cfg] #source [find interface/ftdi/flyswatter2.cfg] # GDB can also flash my flash! gdb_memory_map enable gdb_flash_program enable #source [find target/stm32f1x.cfg] source [find target/stm32xl.cfg] source [find chip/st/stm32/stm32.tcl] proc debug {} { reset halt flash probe 0 stm32f1x mass_erase 0 flash write_bank 0 main.bin 0 reset init } proc run {} { reset halt flash probe 0 stm32f1x mass_erase 0 flash write_bank 0 main.bin 0 reset run } proc samples {} { reset halt flash probe 1 stm32f1x mass_erase 1 flash write_bank 1 samples.bin 0 reset init } |
Compile environment for BrailleBuzz (STM32F103)
Okay three big topics here: crt0 (also known as c0), crt1 (also known as c1), and the linking scripts. crt0 is the code that needs to run before your main, it sets up things like the stack. crt1 is the low level stuff that would normally be OS calls. I sorta simulate an OS because we don’t have one, so I simulate a heap and malloc, etc. The linking scripts tell the linker where to put things in the address space.
crt0
This is the code that executes before your main function gets called. Execution starts at 0000h (actually it may be a pointer on this implementation). But before one can actually run any C one has to do some set-up stuff. Traditionally one writes crt0 in assembly and indeed my original was in assembly. If your careful and don’t use certain C constructs you can write crt0 in c itself. There is heavy interaction with the linker script as we need to prepare things like the BSS, EBSS, stack etc. Remember C is gonna need to know where in the address space (which changes from implementation to implementation) things like RAM(rw), flash(ro) and heap are.
crt0.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
#include <stdlib.h> #include <string.h> #include <stm32f10x_conf.h> #include "irq.h" extern int main(); extern void* _estack; extern void* _sdata; extern void* _edata; extern void* _sidata; extern void* _sbss; extern void* _ebss; extern void* _eusrstack; void _start(); extern void _init_crt1(); /* IRQ vector table */ __attribute__ ((section(".isr_vector"))) struct configword_struct { const void* stackpointer; void (* const vectors[165])(void); } configword = { &_estack, { _start, NMIException, HardFaultException, MemManageException, BusFaultException, UsageFaultException, NULL, NULL, NULL, NULL, SVC_Handler, DebugMon_Handler, NULL, PendSV_Handler, SysTick_Handler, WWDG_IRQHandler, PVD_IRQHandler, TAMPER_IRQHandler, RTC_IRQHandler, FLASH_IRQHandler, RCC_IRQHandler, EXTI0_IRQHandler, EXTI1_IRQHandler, EXTI2_IRQHandler, EXTI3_IRQHandler, EXTI4_IRQHandler, DMA1_Channel1_IRQHandler, DMA1_Channel2_IRQHandler, DMA1_Channel3_IRQHandler, DMA1_Channel4_IRQHandler, DMA1_Channel5_IRQHandler, DMA1_Channel6_IRQHandler, DMA1_Channel7_IRQHandler, ADC1_2_IRQHandler, USB_HP_CAN1_TX_IRQHandler, USB_LP_CAN1_RX0_IRQHandler, CAN1_RX1_IRQHandler, CAN1_SCE_IRQHandler, EXTI9_5_IRQHandler, TIM1_BRK_IRQHandler, TIM1_UP_IRQHandler, TIM1_TRG_COM_IRQHandler, TIM1_CC_IRQHandler, TIM2_IRQHandler, TIM3_IRQHandler, TIM4_IRQHandler, I2C1_EV_IRQHandler, I2C1_ER_IRQHandler, I2C2_EV_IRQHandler, I2C2_ER_IRQHandler, SPI1_IRQHandler, SPI2_IRQHandler, USART1_IRQHandler, USART2_IRQHandler, USART3_IRQHandler, EXTI15_10_IRQHandler, RTCAlarm_IRQHandler, USBWakeUp_IRQHandler, TIM8_BRK_IRQHandler, TIM8_UP_IRQHandler, TIM8_TRG_COM_IRQHandler, TIM8_CC_IRQHandler, ADC3_IRQHandler, FSMC_IRQHandler, SDIO_IRQHandler, TIM5_IRQHandler, SPI3_IRQHandler, UART4_IRQHandler, UART5_IRQHandler, TIM6_IRQHandler, TIM7_IRQHandler, DMA2_Channel1_IRQHandler, DMA2_Channel2_IRQHandler, DMA2_Channel3_IRQHandler, DMA2_Channel4_5_IRQHandler, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; void null_driver_reset() { } void _start() { /* copy data segment from flash to ram */ { const size_t datalen = (size_t) &_edata - (unsigned int) &_sdata; memcpy(&_sdata, &_sidata, datalen); } /* zero out bss segment */ { const size_t bsslen = (size_t) &_ebss - (unsigned int) &_sbss; memset(&_sbss, 0x0, bsslen); } SystemInit(); /* set process stack */ __set_PSP((uint32_t) &_eusrstack); (void) _init_crt1(); (void) main(); } |
crt1
This is the low level operating system type stuff. Well, we don’t have one, but we’d still like things like printf and malloc to work or at least not crash. Remember after you call printf, somewhere down the pike some ascii (or unicode) has to go somewhere. In our case we’re going to output it to one of the UARTs. This is very convenient when debugging! This code took a long time to sort out and is incomplete. I got some of the stubs from the manufacturer. Recall that in a unix OS we would be opening files (in dev), reading from and writing to them but here we don’t have an OS or a file-system so we make some assumptions, lots of assumptions.
crt1.h
1 2 3 4 5 6 |
#ifndef __CRT1_H__ #define __CRT1_H__ void _init_crt1(); #endif /* __CRT1_H__ */ |
crt1.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#include <stdint.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include "stm32f10x_usart.h" #include "irq.h" extern int8_t _sheap; extern int8_t _eheap; static const int8_t* eheap_p = &_eheap; static const int8_t* sheap_p = &_sheap; static intptr_t heap_ptr; size_t heap_peak = 0x0; long _write_r(struct _reent *_r, int fd, const void *buf, int cnt) { int n; const char *ptr = buf; for (n = 0; n < cnt; n++) { while ((USART2->SR & USART_FLAG_TC) == 0x0000); USART2->DR = (*ptr++ & 0x01FF); } return cnt; } long _read_r(struct _reent *_r, int fd, char *ptr, int len ) { while ((USART2->SR & USART_FLAG_RXNE) == 0x0000); *ptr = (char) (USART2->DR & 0x01FF); return 1; } off_t _lseek_r( struct _reent *_r, int fd, off_t pos, int whence ) { return 0; } int _isatty( struct _reent *_r, int fd) { return 1; } int _fstat_r ( struct _reent *_r, int fd, struct stat *pstat) { pstat->st_mode = S_IFCHR; return 0; } long _close_r(struct _reent *_r, int fd) { _r->_errno = EBADF; return -1; } /* int _open_r (struct _reent *_r, const char *file, int flags, int mode) { _r->_errno = ENODEV; return -1; } */ void *_sbrk_r(struct _reent *r, intptr_t incr) { void* retval = 0; intptr_t newbrk = (intptr_t)((size_t)heap_ptr + (size_t)incr); if(newbrk >= (intptr_t)eheap_p) { r->_errno = ENOMEM; retval = (void *)-1; } else { const size_t new_size = ((size_t)newbrk) - ((size_t)sheap_p); heap_peak = (heap_peak < new_size) ? new_size : heap_peak; retval = (void*)heap_ptr; heap_ptr = newbrk; } return retval; } void _init_crt1() { /* set the heap with 0xff */ { const size_t heapsize = ((size_t)(eheap_p)) - ((size_t)sheap_p); memset(&_sheap, 0xff, heapsize); heap_ptr = (const intptr_t)sheap_p; } } void _exit(int i) { while(1); } |
linker script
We need to tell the linker where to put things. What addresses correspond to RAM? What addresses correspond to ROM(flash)? Where’s the “heap”, how big is it.
stm32.ld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
/* default stack sizes. These are used by the startup in order to allocate stacks for the different modes. */ ENTRY(_start) __Stack_Size = 1024 ; PROVIDE ( _Stack_Size = __Stack_Size ) ; __Stack_Init = _estack - __Stack_Size ; /*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/ PROVIDE ( _Stack_Init = __Stack_Init ) ; _Heap_Size = 0x10000; /* There will be a link error if there is not this amount of RAM free at the end. */ _Minimum_Stack_Size = 0x100 ; /* include the memory spaces definitions sub-script */ /* Linker subscript for STM32F10x definitions with 512K Flash and 1024K RAM */ /* Memory Spaces Definitions */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0 EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0 EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0 EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0 EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0 } /* higher address of the user mode stack */ _estack = 0x20017fff; /* include the sections management sub-script for FLASH mode */ /* Sections Definitions */ SECTIONS { /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* the program code is stored in the .text section, which goes to Flash */ .text : { . = ALIGN(4); *(.text) /* remaining code */ *(.text.*) /* remaining code */ *(.rodata) /* read-only data (constants) */ *(.rodata*) *(.glue_7) *(.glue_7t) . = ALIGN(4); _etext = .; /* This is used by the startup in order to initialize the .data secion */ _sidata = _etext; } >FLASH /* This is the initialized data section The program executes knowing that the data is in the RAM but the loader puts the initial values in the FLASH (inidata). It is one task of the startup to copy the initial values from FLASH to RAM. */ .data : AT ( _sidata ) { . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _sdata = . ; *(.data) *(.data.*) . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _edata = . ; } >RAM /* This is the uninitialized data section */ .bss : { . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; *(.bss) *(COMMON) . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _ebss = . ; } >RAM PROVIDE ( end = _ebss ); PROVIDE ( _end = _ebss ); ._usrheap : { _sheap = .; . = . + _Heap_Size; _eheap = .; } >RAM /* This is the user stack section This is just to check that there is enough RAM left for the User mode stack It should generate an error if it's full. */ ._usrstack : { . = ALIGN(4); _susrstack = . ; . = . + _Minimum_Stack_Size ; . = ALIGN(4); _eusrstack = . ; } >RAM /* this is the FLASH Bank1 */ /* the C or assembly source must explicitly place the code or data there using the "section" attribute */ .b1text : { *(.b1text) /* remaining code */ *(.b1rodata) /* read-only data (constants) */ *(.b1rodata*) } >FLASHB1 /* this is the EXTMEM */ /* the C or assembly source must explicitly place the code or data there using the "section" attribute */ /* EXTMEM Bank0 */ .eb0text : { *(.eb0text) /* remaining code */ *(.eb0rodata) /* read-only data (constants) */ *(.eb0rodata*) } >EXTMEMB0 /* EXTMEM Bank1 */ .eb1text : { *(.eb1text) /* remaining code */ *(.eb1rodata) /* read-only data (constants) */ *(.eb1rodata*) } >EXTMEMB1 /* EXTMEM Bank2 */ .eb2text : { *(.eb2text) /* remaining code */ *(.eb2rodata) /* read-only data (constants) */ *(.eb2rodata*) } >EXTMEMB2 /* EXTMEM Bank0 */ .eb3text : { *(.eb3text) /* remaining code */ *(.eb3rodata) /* read-only data (constants) */ *(.eb3rodata*) } >EXTMEMB3 /* after that it's only debugging information. */ /* remove the debugging information from the standard libraries */ DISCARD : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } } |
IRQ handlers
Lastly we have the irq handlers. We mostly just go into an infinite loop. Naturally we really should do better than this but I never got round to it.
irq.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
#ifndef IRQ_H__ #define IRQ_H__ void IRQHandler(void); void NMIException(void); void HardFaultException(void); void MemManageException(void); void BusFaultException(void); void UsageFaultException(void); void SVC_Handler(void); void DebugMon_Handler(void); void PendSV_Handler(void); void SysTick_Handler(void); void WWDG_IRQHandler(void); void PVD_IRQHandler(void); void TAMPER_IRQHandler(void); void RTC_IRQHandler(void); void FLASH_IRQHandler(void); void RCC_IRQHandler(void); void EXTI0_IRQHandler(void); void EXTI1_IRQHandler(void); void EXTI2_IRQHandler(void); void EXTI3_IRQHandler(void); void EXTI4_IRQHandler(void); void DMA1_Channel1_IRQHandler(void); void DMA1_Channel2_IRQHandler(void); void DMA1_Channel3_IRQHandler(void); void DMA1_Channel4_IRQHandler(void); void DMA1_Channel5_IRQHandler(void); void DMA1_Channel6_IRQHandler(void); void DMA1_Channel7_IRQHandler(void); void ADC1_2_IRQHandler(void); void USB_HP_CAN1_TX_IRQHandler(void); void USB_LP_CAN1_RX0_IRQHandler(void); void CAN1_RX1_IRQHandler(void); void CAN1_SCE_IRQHandler(void); void EXTI9_5_IRQHandler(void); void TIM1_BRK_IRQHandler(void); void TIM1_UP_IRQHandler(void); void TIM1_TRG_COM_IRQHandler(void); void TIM1_CC_IRQHandler(void); void TIM2_IRQHandler(void); void TIM3_IRQHandler(void); void TIM4_IRQHandler(void); void I2C1_EV_IRQHandler(void); void I2C1_ER_IRQHandler(void); void I2C2_EV_IRQHandler(void); void I2C2_ER_IRQHandler(void); void SPI1_IRQHandler(void); void SPI2_IRQHandler(void); void USART1_IRQHandler(void); void USART2_IRQHandler(void); void USART3_IRQHandler(void); void EXTI15_10_IRQHandler(void); void RTCAlarm_IRQHandler(void); void USBWakeUp_IRQHandler(void); void TIM8_BRK_IRQHandler(void); void TIM8_UP_IRQHandler(void); void TIM8_TRG_COM_IRQHandler(void); void TIM8_CC_IRQHandler(void); void ADC3_IRQHandler(void); void FSMC_IRQHandler(void); void SDIO_IRQHandler(void); void TIM5_IRQHandler(void); void SPI3_IRQHandler(void); void UART4_IRQHandler(void); void UART5_IRQHandler(void); void TIM6_IRQHandler(void); void TIM7_IRQHandler(void); void DMA2_Channel1_IRQHandler(void); void DMA2_Channel2_IRQHandler(void); void DMA2_Channel3_IRQHandler(void); void DMA2_Channel4_5_IRQHandler(void); #endif // IRQ_H__ |
irq.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
#include <stdint.h> #include <stdio.h> #include "irq.h" extern void _start(); void SVC_Handler_main(uint32_t* svc_args) { uint32_t svc_number; svc_number = ((char *) svc_args[6])[-2]; switch (svc_number) { case 0: break; default: break; } } void NMIException(void) { for (;;) ; } void HardFaultException(void) { for (;;) ; } void MemManageException(void) { for (;;) ; } void BusFaultException(void) { for (;;) ; } void UsageFaultException(void) { for (;;) ; } void DebugMon_Handler(void) { for (;;) ; } void PendSV_Handler(void) { for (;;) ; } void SysTick_Handler(void) { for (;;) ; } void WWDG_IRQHandler(void) { for (;;) ; } void PVD_IRQHandler(void) { for (;;) ; } void TAMPER_IRQHandler(void) { for (;;) ; } void RTC_IRQHandler(void) { for (;;) ; } void FLASH_IRQHandler(void) { for (;;) ; } void RCC_IRQHandler(void) { for (;;) ; } void EXTI0_IRQHandler(void) { for (;;) ; } void EXTI1_IRQHandler(void) { for (;;) ; } void EXTI2_IRQHandler(void) { for (;;) ; } void EXTI3_IRQHandler(void) { for (;;) ; } void EXTI4_IRQHandler(void) { for (;;) ; } void DMA1_Channel1_IRQHandler(void) { for (;;) ; } void DMA1_Channel2_IRQHandler(void) { for (;;) ; } void DMA1_Channel3_IRQHandler(void) { for (;;) ; } void DMA1_Channel4_IRQHandler(void) { for (;;) ; } void DMA1_Channel5_IRQHandler(void) { for (;;) ; } void DMA1_Channel6_IRQHandler(void) { for (;;) ; } void DMA1_Channel7_IRQHandler(void) { for (;;) ; } void ADC1_2_IRQHandler(void) { for (;;) ; } void USB_HP_CAN1_TX_IRQHandler(void) { for (;;) ; } void USB_LP_CAN1_RX0_IRQHandler(void) { for (;;) ; } void CAN1_RX1_IRQHandler(void) { for (;;) ; } void CAN1_SCE_IRQHandler(void) { for (;;) ; } void EXTI9_5_IRQHandler(void) { for (;;) ; } void TIM1_BRK_IRQHandler(void) { for (;;) ; } void TIM1_UP_IRQHandler(void) { for (;;) ; } void TIM1_TRG_COM_IRQHandler(void) { for (;;) ; } void TIM1_CC_IRQHandler(void) { for (;;) ; } void TIM2_IRQHandler(void) { for (;;) ; } void TIM3_IRQHandler(void) { for (;;) ; } void TIM4_IRQHandler(void) { for (;;) ; } void I2C1_EV_IRQHandler(void) { for (;;) ; } void I2C1_ER_IRQHandler(void) { for (;;) ; } void I2C2_EV_IRQHandler(void) { for (;;) ; } void I2C2_ER_IRQHandler(void) { for (;;) ; } void SPI1_IRQHandler(void) { for (;;) ; } void SPI2_IRQHandler(void) { for (;;) ; } void USART1_IRQHandler(void) { for (;;) ; } void USART2_IRQHandler(void) { } void USART3_IRQHandler(void) { for (;;) ; } void EXTI15_10_IRQHandler(void) { for (;;) ; } void RTCAlarm_IRQHandler(void) { for (;;) ; } void USBWakeUp_IRQHandler(void) { for (;;) ; } void TIM8_BRK_IRQHandler(void) { for (;;) ; } void TIM8_UP_IRQHandler(void) { for (;;) ; } void TIM8_TRG_COM_IRQHandler(void) { for (;;) ; } void TIM8_CC_IRQHandler(void) { for (;;) ; } void ADC3_IRQHandler(void) { for (;;) ; } void FSMC_IRQHandler(void) { for (;;) ; } void SDIO_IRQHandler(void) { for (;;) ; } void TIM5_IRQHandler(void) { for (;;) ; } void SPI3_IRQHandler(void) { for (;;) ; } void UART4_IRQHandler(void) { for (;;) ; } void UART5_IRQHandler(void) { for (;;) ; } void TIM6_IRQHandler(void) { for (;;) ; } void TIM7_IRQHandler(void) { for (;;) ; } void DMA2_Channel1_IRQHandler(void) { for (;;) ; } void DMA2_Channel2_IRQHandler(void) { for (;;) ; } void DMA2_Channel3_IRQHandler(void) { for (;;) ; } void DMA2_Channel4_5_IRQHandler(void) { for (;;) ; } |
Build environment for BrailleBuzz (the GNU tool-chain for STM32F103)
I am documenting from memory here, I did this over a year ago and have just now gotten round to documenting it. There are a bunch of things I gloss over and one or two inaccuracies that I am miss-remembering. Nevertheless, this should give you a clue and along with the excellent documentation over at gnu.org it should get you going. Of particular use are my bash scripts, read them, understand them, and optionally use them verbatim. Also use the docs on the olmex site. I used those development boards to bootstrap this project they are excellent! I don’t know if it’s the “right” way but what I did was to create a usr directory off my home directory. While I have root on my box, and everything else around here, my goal was to avoid using it so that I could simply zip it up and hand it off to another developer; which is what I have recently done.
tallen@timmy:~/usr$ ls -l
total 32
drwxrwxr-x 5 tallen tallen 4096 Jan 24 2014 arm-none-eabi
drwxrwxr-x 2 tallen tallen 4096 Mar 24 2014 bin
drwxrwxr-x 12 tallen tallen 4096 Feb 26 2014 build
drwxrwxr-x 3 tallen tallen 4096 Mar 17 2014 include
drwxrwxr-x 3 tallen tallen 4096 Mar 24 2014 lib
drwxrwxr-x 3 tallen tallen 4096 Jan 24 2014 libexec
drwxrwxr-x 9 tallen tallen 4096 Feb 12 2014 share
drwxrwxr-x 11 tallen tallen 4096 Mar 24 2014 src
Under src one puts the source for the various tools, e.g. binutils, gcc, gdb, openocd, speex, and gmp, mpc, mpfr; the latter three are prerequisites for gcc.
tallen@timmy:~/usr/src$ ls -l
total 138384
drwxrwxr-x 17 tallen tallen 4096 Jan 24 2014 binutils-2.24
-rw-rw-r-- 1 tallen tallen 22716802 Dec 2 2013 binutils-2.24.tar.bz2
drwxr-xr-x 33 tallen tallen 4096 Oct 16 2013 gcc-4.8.2
-rw-rw-r-- 1 tallen tallen 85999682 Oct 16 2013 gcc-4.8.2.tar.bz2
drwxrwxr-x 15 tallen tallen 4096 Feb 11 2014 gdb-7.7
-rw-rw-r-- 1 tallen tallen 24846320 Feb 5 2014 gdb-7.7.tar.bz2
drwxr-xr-x 15 tallen tallen 4096 Sep 30 2013 gmp-5.1.3
-rw-rw-r-- 1 tallen tallen 1818812 Sep 30 2013 gmp-5.1.3.tar.xz
drwxr-xr-x 6 tallen tallen 4096 Jan 15 2014 mpc-1.0.2
-rw-rw-r-- 1 tallen tallen 633173 Jan 15 2014 mpc-1.0.2.tar.gz
drwxr-xr-x 9 tallen tallen 4096 Mar 13 2013 mpfr-3.1.2
-rw-rw-r-- 1 tallen tallen 1074388 Mar 13 2013 mpfr-3.1.2.tar.xz
drwxrwxr-x 9 tallen tallen 4096 Mar 24 2014 newlib
drwxr-xr-x 8 tallen tallen 4096 May 5 2013 openocd-0.7.0
-rw------- 1 tallen tallen 3493924 Feb 10 2014 openocd-0.7.0.tar.bz2
drwxrwxr-x 9 tallen tallen 4096 Apr 7 2014 speex-1.2rc1
-rw-rw-r-- 1 tallen tallen 1061882 Jul 23 2008 speex-1.2rc1.tar.gz
Next one creates a build directory under usr. Under build one creates directories for each of the utilities enumerated below. I also played some games with soft links to facilitate version upgrades later:
tallen@timmy:~/usr/build$ ls -l
total 168
lrwxrwxrwx 1 tallen tallen 14 Jan 24 2014 binutils -> binutils-2.24/
drwxrwxr-x 11 tallen tallen 4096 Feb 18 2014 binutils-2.24
lrwxrwxrwx 1 tallen tallen 11 Jan 24 2014 gcc_1 -> gcc-4.8.2_1
lrwxrwxrwx 1 tallen tallen 11 Jan 24 2014 gcc_2 -> gcc-4.8.2_2
drwxrwxr-x 14 tallen tallen 4096 Feb 18 2014 gcc-4.8.2_1
drwxrwxr-x 12 tallen tallen 4096 Mar 24 2014 gcc-4.8.2_2
-rw-rw-r-- 1 tallen tallen 129742 Feb 10 2014 gcc_arm-eabi_build.pdf
lrwxrwxrwx 1 tallen tallen 7 Feb 11 2014 gdb -> gdb-7.7
drwxrwxr-x 11 tallen tallen 4096 Feb 11 2014 gdb-7.7
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 gmp -> gmp-5.1.3/
drwxrwxr-x 15 tallen tallen 4096 May 29 14:20 gmp-5.1.3
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 mpc -> mpc-1.0.2/
drwxrwxr-x 5 tallen tallen 4096 Apr 7 2014 mpc-1.0.2
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 mpfr -> mpfr-3.1.2
drwxrwxr-x 6 tallen tallen 4096 Mar 17 2014 mpfr-3.1.2
drwxrwxr-x 4 tallen tallen 4096 Apr 7 2014 newlib
lrwxrwxrwx 1 tallen tallen 14 Feb 10 2014 openocd -> openocd-0.7.0/
drwxrwxr-x 5 tallen tallen 4096 Feb 11 2014 openocd-0.7.0
lrwxrwxrwx 1 tallen tallen 12 Feb 26 2014 speex -> speex-1.2rc1
drwxrwxr-x 9 tallen tallen 4096 Apr 9 2014 speex-1.2rc1
binutils
Binutils includes things like the assembler, you do remember that the assembler is the back-end for gcc; that is, gcc really just produces assembly which is fed to the assembler, the linker, the archiver, and other tools. There are practical reasons why these steps are combined under the gcc command line interface. One creates a directory in which to one builds the binutils. I created a small bash script.
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/bash ../../src/binutils-2.24/configure --target=arm-none-eabi --prefix=$HOME/usr --enable-interwork --enable-multilib --with-gcc --with-gnu-as --with-gnu-ld --disable-nls --disable-shared --disable-werror |
Don’t forget to do “make install”
gmp
One creates a directory in which to one builds gmp. I created a small bash script.
1 2 3 4 5 |
#!/bin/bash ../../src/gmp-5.1.3/configure --disable-shared --enable-static --prefix=$HOME/usr |
mpc
One creates a directory in which to one builds mpc. I created a small bash script.
1 2 3 4 5 6 7 |
#!/bin/bash ../../src/mpc-1.0.2/configure --prefix=$HOME/usr --with-gmp=$HOME/usr --with-mpfr=$HOME/usr --disable-shared --enable-static |
mpfr
One creates a directory in which to one builds mpfr. I created a small bash script.
1 2 3 4 5 6 |
#!/bin/bash ../../src/mpfr-3.1.2/configure --prefix=$HOME/usr --with-gmp=$HOME/usr --disable-shared --enable-static |
gcc
One builds gcc in two steps and the previous tasks are prerequisites. Let’s take a moment and think about what we’re actually doing here. We are using the host’s native gcc compiler to build a cross compiler from (more or less) the same source that built the native compiler (look up what GNU actually stands for and see if you get the joke); that is, a compiler that runs on one arch (the host, probably an Intel variant) but produces code that will run on another arch (the target, in this case, an ARM variant). To make things more confusing the compiler actually needs itself. So one builds a striped down version of gcc (just C), and then one builds the full version of gcc (C & C++). Confused yet, no? GNUs not unix… GNUs not unix…
gcc_1
One creates a directory in which to one builds the stripped down gcc, gcc_1. I created a small bash script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/bin/bash -x ../../src/gcc-4.8.2/configure --target=arm-none-eabi --prefix=$HOME/usr --enable-interwork --enable-multilib --disable-libssp --enable-languages="c" --with-newlib --without-headers --disable-shared --disable-threads --with-gnu-as --disable-nls --with-gnu-ld --with-gmp=$HOME/usr --with-mpfr=$HOME/usr --with-mpc=$HOME/usr |
Don’t forget to do “make install”
newlib or libc
You’ll need newlib or libc to build the full compiler. The full compiler needs libc stuff for it’s built-ins. One needs to build newlib, this is a slightly stripped down version of libc especially for MCUs and SOCs. So use the stripped down gcc to build newlib, I created a small bash script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/bin/bash ../../src/newlib/configure --target=arm-none-eabi --prefix=$HOME/usr --enable-interwork --enable-multilib --with-float=soft --enable-soft-float --disable-nls --with-gnu-ld --with-gnu-as --disable-shared CFLAGS_FOR_TARGET=" -g -Ofast -ffunction-sections -fdata-sections -DPREFER_SIZE_OVER_SPEED -D__OPTIMIZE_SIZE__ -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -mfix-cortex-m3-ldrd -D__thumb2__ -D__BUFSIZ__=256" CCASFLAGS="-mcpu=cortex-m3 -mthumb -mfix-cortex-m3-ldrd -D__thumb2__" |
gcc_1
Then one builds the fill compiler in the directory gcc_2. I created a small bash script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/bash -x ../../src/gcc-4.8.2/configure --target=arm-none-eabi --prefix=$HOME/usr --enable-interwork --enable-multilib --disable-libssp --enable-languages="c,c++" --with-newlib --disable-shared --with-float=soft --with-cpu=cortex-m3 --with-tune=cortex-m3 --with-mode=thumb --with-system-zlib --with-gnu-as --with-gnu-ld --disable-nls --with-gmp=$HOME/usr --with-mpfr=$HOME/usr --with-mpc=$HOME/usr |
Notice the “–with-newlib” the full compiler needs libc stuff for it’s built-ins. One needs to build newlib, this is a slightly stripped down version of libc especially for MCUs and SOCs. So use the stripped down gcc to build newlib, then build the full version of gcc and then re-build newlib.
gdb
Your gonna want a debugger and if doesn’t get any better than gdb. Okay, it’s got a steep learning curve, the the curses stuff has issues, but it has features. Perhaps my favorite feature is that it works more or less the same on whatever platform and I don’t know about you but I haven’t the time to learn a bunch of proprietary UIs. GDB works with openocd to do remote debugging.
1 2 3 4 5 6 7 8 |
<pre class="lang:default decode:true " > #!/bin/bash ../../src/gdb-7.7/configure --prefix=$HOME/usr --target=arm-none-eabi --enable-interwork --enable-multilib </pre> |
openocd
I had to build openocd from scratch to get a workable version. This was over a year ago and openocd was very young, it has matured a lot and the stock copy that comes with your distro will probably work for you nowadays, I include it here just for completeness.
1 2 3 4 |
#!/bin/bash ../../src/openocd-0.7.0/configure --enable-ft2232_libftdi --prefix=$HOME/usr |
Embedded Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
CC = arm-none-eabi-gcc LD = arm-none-eabi-gcc AR = arm-none-eabi-ar AS = arm-none-eabi-as CP = arm-none-eabi-objcopy OD = arm-none-eabi-objdump DRIVER = STM32_USB-FS-Device_Lib_V4.0.0/Libraries/STM32F10x_StdPeriph_Driver CMSIS = STM32_USB-FS-Device_Lib_V4.0.0/Libraries/CMSIS CMSISD = STM32_USB-FS-Device_Lib_V4.0.0/Libraries/CMSIS/Device/ST/STM32F10x libdir = lib CFLAGS = -I. -I$(DRIVER)/inc -I$(CMSIS)/Include -I$(CMSISD)/Include -DSTM32F10X_XL -DUSE_STDPERIPH_DRIVER -c -fno-common -O0 -g -march=armv7-m -mcpu=cortex-m3 -mthumb -mthumb-interwork --std=gnu99 AFLAGS = -mapcs-32 LFLAGS = -nostartfiles CPFLAGS = -Obinary ODFLAGS = -S libs = $(libdir)/libstdperiphdriver.a $(libdir)/libcmsisd.a all: libs main .PHONY : libs libs: $(libs) clean: -rm -f main.lst main.bin main.elf main.hex main.map *.o $(libdir)/* ################# # libraries ################# $(libdir)/libstdperiphdriver.a: stm32f10x_rcc.o stm32f10x_gpio.o stm32f10x_usart.o stm32f10x_exti.o stm32f10x_dac.o stm32f10x_tim.o stm32f10x_dma.o stm32f10x_pwr.o $(AR) rvs $@ $^ stm32f10x_rcc.o: $(DRIVER)/src/stm32f10x_rcc.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_gpio.o: $(DRIVER)/src/stm32f10x_gpio.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_usart.o: $(DRIVER)/src/stm32f10x_usart.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_exti.o: $(DRIVER)/src/stm32f10x_exti.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_dac.o: $(DRIVER)/src/stm32f10x_dac.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_tim.o: $(DRIVER)/src/stm32f10x_tim.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_dma.o: $(DRIVER)/src/stm32f10x_dma.c @ echo $(CC) $(CFLAGS) $^ stm32f10x_pwr.o: $(DRIVER)/src/stm32f10x_pwr.c @ echo $(CC) $(CFLAGS) $^ $(libdir)/libcmsisd.a: system_stm32f10x.o $(AR) rvs $@ $^ system_stm32f10x.o: $(CMSISD)/Source/Templates/system_stm32f10x.c @ echo $(CC) $(CFLAGS) $^ main: main.elf @ echo "...copying" $(CP) $(CPFLAGS) main.elf main.bin $(OD) $(ODFLAGS) main.elf > main.lst main.elf: stm32.ld main.o crt0.o crt1.o irq.o svc.o $(libs) @ echo $(LD) $(LFLAGS) -T$^ speex/lib/libspeex.a -o$@ crt0.o: crt0.c @ echo $(CC) $(CFLAGS) $^ crt1.o: crt1.c @ echo $(CC) $(CFLAGS) $^ irq.o: irq.c @ echo $(CC) $(CFLAGS) $^ svc.o: svc.s @ echo $(AS) $(AFLAGS) $^ -o$@ main.o: main.c @ echo $(CC) $(CFLAGS) $^ samples.bin: samples/samples.raw dd if=$^ of=$@ bs=1 count=512K samples/samples.raw: samples/samples.wav sox -v.9 $^ -c1 -r8k -b8 -eunsigned-integer $@ tags: Makefile ctags-exuberant --c-kinds=+p --c++-kinds=+p --fields=+iaS --extra=+q * \ $(DRIVER)/src/* $(DRIVER)/inc/* \ $(CMSIS)/Include/* \ $(CMSISD)/Include/* \ speex/* \ ~/usr/arm-none-eabi/include/* |