+++ /dev/null
-BINARY=test
-OBJS=test.o util-debug.o ds18b20.o
-
-OPENCM3_DIR=/home/mj/stm/libopencm3
-DEVICE=stm32f103x8
-
-all: $(BINARY).elf $(BINARY).bin
-
-flash: all
- ../bin/st-flash write $(BINARY).bin 0x8000000
-
-reset: all
- ../bin/st-flash reset
-
-ifneq ($(V),1)
-Q := @
-NULL := 2>/dev/null
-endif
-
-include $(OPENCM3_DIR)/mk/genlink-config.mk
-
-PREFIX ?= arm-none-eabi
-
-CC := $(PREFIX)-gcc
-CXX := $(PREFIX)-g++
-LD := $(PREFIX)-gcc
-AR := $(PREFIX)-ar
-AS := $(PREFIX)-as
-OBJCOPY := $(PREFIX)-objcopy
-OBJDUMP := $(PREFIX)-objdump
-GDB := $(PREFIX)-gdb
-OPT := -Os
-DEBUG := -ggdb3
-CSTD ?= -std=c99
-
-TGT_CFLAGS += $(OPT) $(CSTD) $(DEBUG)
-TGT_CFLAGS += $(ARCH_FLAGS)
-TGT_CFLAGS += -Wall -Wextra -Wshadow -Wimplicit-function-declaration
-TGT_CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
-TGT_CFLAGS += -fno-common -ffunction-sections -fdata-sections
-
-TGT_CPPFLAGS += -MD
-
-TGT_LDFLAGS += --static -nostartfiles
-TGT_LDFLAGS += -T$(LDSCRIPT)
-TGT_LDFLAGS += $(ARCH_FLAGS) $(DEBUG)
-TGT_LDFLAGS += -Wl,-Map=$(*).map -Wl,--cref
-TGT_LDFLAGS += -Wl,--gc-sections
-ifeq ($(V),99)
-TGT_LDFLAGS += -Wl,--print-gc-sections
-endif
-
-LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
-
-include $(OPENCM3_DIR)/mk/genlink-rules.mk
-
-%.bin: %.elf
- @printf " OBJCOPY $(*).bin\n"
- $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin
-
-%.elf: $(OBJS) $(LDSCRIPT)
- @printf " LD $(*).elf\n"
- $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $*.elf
-
-%.o: %.c
- @printf " CC $(*).c\n"
- $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $*.o -c $*.c
-
-.PHONY: clean
-clean:
- @printf " CLEAN\n"
- $(Q)rm -f *.elf *.bin *.o *.d *.map $(LDSCRIPT)
-
-%.stlink-flash: %.bin
- @printf " FLASH $<\n"
- $(STFLASH) write $(*).bin 0x8000000
-
-.SECONDEXPANSION:
-.SECONDARY:
-
--include $(OBJS:.o=.d)
+++ /dev/null
-// DS18B20 Temperature Sensors
-
-#include "util.h"
-#include "ds18b20.h"
-
-#include <libopencm3/cm3/cortex.h>
-#include <libopencm3/stm32/dma.h>
-#include <libopencm3/stm32/gpio.h>
-#include <libopencm3/stm32/rcc.h>
-#include <libopencm3/stm32/timer.h>
-#include <string.h>
-
-static volatile u32 ds_dma_buffer;
-
-#define DS_TIMER TIM3
-#define DS_GPIO GPIOA
-#define DS_PIN GPIO7
-#define DS_DMA DMA1
-#define DS_DMA_CH 6
-
-#define DS_DEBUG
-#undef DS_DEBUG2
-
-#ifdef DS_DEBUG
-#define DEBUG debug_printf
-#else
-#define DEBUG(xxx, ...) do { } while (0)
-#endif
-
-#ifdef DS_DEBUG2
-#define DEBUG2 debug_printf
-#else
-#define DEBUG2(xxx, ...) do { } while (0)
-#endif
-
-// Current temperature
-int ds_current_temp = DS_TEMP_UNKNOWN;
-
-// Auxiliary functions which are missing from libopencm3
-static inline bool timer_is_counter_enabled(u32 timer)
-{
- return TIM_CR1(timer) & TIM_CR1_CEN;
-}
-
-static inline void timer_enable_dma_cc1(u32 timer)
-{
- TIM_DIER(timer) |= TIM_DIER_CC1DE;
-}
-
-static inline void timer_disable_dma_cc1(u32 timer)
-{
- TIM_DIER(timer) &= ~TIM_DIER_CC1DE;
-}
-
-static bool ds_reset(void)
-{
- DEBUG2("DS18B20: Reset\n");
- timer_disable_counter(DS_TIMER);
- timer_one_shot_mode(DS_TIMER);
-
- // DMA for reading pin state
- ds_dma_buffer = 0xdeadbeef;
- dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer);
- dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO));
- dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
- dma_enable_channel(DS_DMA, DS_DMA_CH);
-
- // CC1 is used to drive the DMA (read line state at specified time)
- timer_disable_oc_output(DS_TIMER, TIM_OC1);
- timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN);
- timer_set_oc_value(DS_TIMER, TIM_OC1, 560);
- timer_set_dma_on_compare_event(DS_TIMER);
- timer_enable_dma_cc1(DS_TIMER);
-
- // CC2 is used to generate pulses (return line to idle state at specified time)
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
- timer_enable_oc_output(DS_TIMER, TIM_OC2);
- timer_set_oc_value(DS_TIMER, TIM_OC2, 480);
- timer_set_oc_polarity_low(DS_TIMER, TIM_OC2);
-
- // Set timer period to the length of the whole transaction (1 ms)
- timer_set_period(DS_TIMER, 999);
-
- // Pull line down and start timer
- cm_disable_interrupts();
- timer_enable_counter(DS_TIMER);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
- cm_enable_interrupts();
-
- // Wait until the timer expires
- while (timer_is_counter_enabled(DS_TIMER))
- ;
- // Counter is automatically disabled at the end of cycle
-
- // Disable DMA
- timer_disable_dma_cc1(DS_TIMER);
- dma_disable_channel(DS_DMA, DS_DMA_CH);
-
- DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
- ds_dma_buffer,
- !!(ds_dma_buffer & GPIO7),
- dma_get_number_of_data(DS_DMA, DS_DMA_CH));
-
- // Did the device respond?
- if (ds_dma_buffer & GPIO7) {
- DEBUG("DS18B20: Initialization failed\n");
- return 0;
- } else
- return 1;
-}
-
-static void ds_send_bit(bool bit)
-{
- timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
- timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse
- cm_disable_interrupts();
- // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
- // otherwise OC triggers immediately. Reasons?
- timer_enable_counter(DS_TIMER);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
- cm_enable_interrupts();
- while (timer_is_counter_enabled(DS_TIMER))
- ;
-}
-
-static void ds_send_byte(byte b)
-{
- DEBUG2("DS write: %02x\n", b);
- for (uint m = 1; m < 0x100; m <<= 1)
- ds_send_bit(b & m);
-}
-
-static bool ds_recv_bit(void)
-{
- timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs
- timer_set_oc_value(DS_TIMER, TIM_OC2, 2); // Generate 2μs pulse to start read slot
- timer_set_oc_value(DS_TIMER, TIM_OC1, 8); // Sample data 8μs after start of slot
- timer_enable_dma_cc1(DS_TIMER);
-
- ds_dma_buffer = 0xdeadbeef;
- dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
- dma_enable_channel(DS_DMA, DS_DMA_CH);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
- cm_disable_interrupts();
- timer_enable_counter(DS_TIMER);
- timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
- cm_enable_interrupts();
- while (timer_is_counter_enabled(DS_TIMER))
- ;
- // DEBUG2("XXX %08x\n", ds_dma_buffer);
- bool out = ds_dma_buffer & GPIO7;
- dma_disable_channel(DS_DMA, DS_DMA_CH);
-
- timer_disable_dma_cc1(DS_TIMER);
-
- return out;
-}
-
-static byte ds_recv_byte(void)
-{
- uint out = 0;
- for (uint m = 1; m < 0x100; m <<= 1) {
- if (ds_recv_bit())
- out |= m;
- }
-
- DEBUG2("DS read: %02x\n", out);
- return out;
-}
-
-static byte ds_buf[10];
-
-static byte ds_crc_block(uint n)
-{
- /// XXX: This might be worth optimizing
- uint crc = 0;
-
- for (uint i = 0; i < n; i++) {
- byte b = ds_buf[i];
- for (uint j = 0; j < 8; j++) {
- uint k = (b & 1) ^ (crc >> 7);
- crc = (crc << 1) & 0xff;
- if (k)
- crc ^= 0x31;
- b >>= 1;
- }
- }
-
- return crc;
-}
-
-static bool ds_recv_block(uint n)
-{
- for (uint i = 0; i < n; i++)
- ds_buf[i] = ds_recv_byte();
-
- byte crc = ds_crc_block(n);
- if (crc) {
- DEBUG("DS18B20: Invalid CRC %02x\n", crc);
- return 0;
- }
- return 1;
-}
-
-struct ds_sensor ds_sensors[DS_NUM_SENSORS];
-
-#if DS_NUM_SENSORS == 1
-
-static void ds_enumerate(void)
-{
- if (!ds_reset())
- return;
-
- ds_send_byte(0x33); // READ_ROM
- if (!ds_recv_block(8))
- return;
-
- DEBUG("DS18B20: Found sensor ");
- for (uint i = 0; i < 8; i++) {
- DEBUG("%02x", ds_buf[i]);
- ds_sensors[0].address[i] = ds_buf[i];
- }
- DEBUG("\n");
-}
-
-#else
-
-static void ds_enumerate(void)
-{
- /*
- * The enumeration algorithm roughly follows the one described in the
- * Book of iButton Standards (Maxim Integrated Application Note 937).
- *
- * It simulates depth-first search on the trie of all device IDs.
- * In each pass, it walks the trie from the root and recognizes branching nodes.
- *
- * The old_choice variable remembers the deepest left branch taken in the
- * previous pass, new_choice is the same for the current pass.
- */
-
- DEBUG("DS18B20: Enumerate\n");
-
- uint num_sensors = 0;
- byte *addr = ds_buf;
- byte old_choice = 0;
-
- for (;;) {
- if (!ds_reset()) {
- DEBUG("DS18B20: Enumeration found no sensor\n");
- return;
- }
-
- ds_send_byte(0xf0); // SEARCH_ROM
- byte new_choice = 0;
- for (byte i=0; i<64; i++) {
- bool have_one = ds_recv_bit();
- bool have_zero = ds_recv_bit();
- bool old_bit = addr[i/8] & (1U << (i%8));
- bool new_bit;
- switch (2*have_one + have_zero) {
- case 3:
- // This should not happen
- DEBUG("DS18B20: Enumeration failed\n");
- return;
- case 1:
- // Only 0
- new_bit = 0;
- break;
- case 2:
- // Only 1
- new_bit = 1;
- break;
- default:
- // Both
- if (i == old_choice)
- new_bit = 1;
- else if (i > old_choice) {
- new_bit = 0;
- new_choice = i;
- } else {
- new_bit = old_bit;
- if (!new_bit)
- new_choice = i;
- }
- }
- if (new_bit)
- addr[i/8] |= 1U << (i%8);
- else
- addr[i/8] &= ~(1U << (i%8));
- ds_send_bit(new_bit);
- }
-
- if (num_sensors >= DS_NUM_SENSORS) {
- DEBUG("DS18B20: Too many sensors\n");
- return;
- }
-
- DEBUG("DS18B20: Found sensor #%u: ", num_sensors);
- for (byte i=0; i<8; i++)
- DEBUG("%02x", addr[i]);
- if (ds_crc_block(8)) {
- DEBUG(" - invalid CRC!\n");
- } else if (ds_buf[0] == 0x28) {
- DEBUG("\n");
- memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
- num_sensors++;
- } else {
- DEBUG(" - wrong type\n");
- }
-
- old_choice = new_choice;
- if (!old_choice)
- break;
- }
-}
-
-#endif
-
-void ds_init(void)
-{
- DEBUG("DS18B20: Init\n");
-
- for (uint i = 0; i < DS_NUM_SENSORS; i++) {
- memset(ds_sensors[i].address, 0, 8);
- ds_sensors[i].current_temp = DS_TEMP_UNKNOWN;
- }
-
- dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
- dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
- dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
- dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH);
- dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT);
- dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT);
-
- timer_set_prescaler(DS_TIMER, 71); // 1 tick = 1 μs
- timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
- timer_disable_preload(DS_TIMER);
-
- gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7);
-
- ds_enumerate();
-
- // FIXME: Configure precision?
-}
-
-#if DS_NUM_SENSORS == 1
-#define ds_current_id 0
-#else
- static byte ds_current_id;
-#endif
-
-static bool ds_activate(void)
-{
- if (!ds_reset()) {
- DEBUG("DS18B20: Reset failed\n");
- return false;
- }
-#if DS_NUM_SENSORS == 1
- ds_send_byte(0xcc); // SKIP_ROM
-#else
- ds_send_byte(0x55); // MATCH_ROM
- for (uint i = 0; i < 8; i++)
- ds_send_byte(ds_sensors[ds_current_id].address[i]);
-#endif
- return true;
-}
-
-void ds_step(void)
-{
- static byte ds_running;
- static byte ds_timeout;
-
- if (!ds_running) {
- // Start measurement
-#if DS_NUM_SENSORS != 1
- uint maxn = DS_NUM_SENSORS;
- do {
- if (!maxn--)
- return;
- ds_current_id++;
- if (ds_current_id >= DS_NUM_SENSORS)
- ds_current_id = 0;
- } while (!ds_sensors[ds_current_id].address[0]);
-#endif
- if (!ds_activate()) {
- ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
- return;
- }
- ds_send_byte(0x44); // CONVERT_T
- ds_running = 1;
- ds_timeout = 255;
- } else {
- // Still running?
- if (!ds_recv_bit()) {
- if (!ds_timeout--) {
- DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
- ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
- ds_running = 0;
- }
- return;
- }
- ds_running = 0;
-
- // Read scratch pad
- if (!ds_activate())
- return;
- ds_send_byte(0xbe); // READ_SCRATCHPAD
- if (!ds_recv_block(9)) {
- ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
- return;
- }
- int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
- t = t * 1000 / 16;
-
- DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
- ds_sensors[ds_current_id].current_temp = t;
- }
-}
+++ /dev/null
-// DS18B20 Temperature Sensors
-
-#ifndef _DS18B20_H
-#define _DS18B20_H
-
-// Maximum number of supported sensors
-#define DS_NUM_SENSORS 8
-
-struct ds_sensor {
- byte address[8]; // All zeroes if sensor does not exist.
- // Otherwise, address[0] is guaranteed to be non-zero.
- int current_temp; // Temperature in m°C or DS_TEMP_UNKNOWN
-};
-
-extern struct ds_sensor ds_sensors[DS_NUM_SENSORS];
-
-#define DS_TEMP_UNKNOWN 0x7fffffff
-
-void ds_init(void);
-void ds_step(void);
-
-#endif
+++ /dev/null
-#include "util.h"
-#include "ds18b20.h"
-
-#include <libopencm3/cm3/nvic.h>
-#include <libopencm3/cm3/systick.h>
-#include <libopencm3/stm32/rcc.h>
-#include <libopencm3/stm32/gpio.h>
-#include <libopencm3/stm32/usart.h>
-
-static void clock_setup(void)
-{
- rcc_clock_setup_in_hse_8mhz_out_72mhz();
-
- rcc_periph_clock_enable(RCC_GPIOA);
- rcc_periph_clock_enable(RCC_GPIOC);
- rcc_periph_clock_enable(RCC_USART1);
- rcc_periph_clock_enable(RCC_TIM3);
- rcc_periph_clock_enable(RCC_DMA1);
-}
-
-static void gpio_setup(void)
-{
- gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
-}
-
-static volatile u32 ms_ticks;
-
-void sys_tick_handler(void)
-{
- ms_ticks++;
-}
-
-static void tick_setup(void)
-{
- systick_set_frequency(1000, 72000000);
- systick_counter_enable();
- systick_interrupt_enable();
-}
-
-static void delay_ms(uint ms)
-{
- u32 start_ticks = ms_ticks;
- while (ms_ticks - start_ticks < ms)
- ;
-}
-
-static void usart_setup(void)
-{
- gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
-
- usart_set_baudrate(USART1, 115200);
- usart_set_databits(USART1, 8);
- usart_set_stopbits(USART1, USART_STOPBITS_1);
- usart_set_mode(USART1, USART_MODE_TX);
- usart_set_parity(USART1, USART_PARITY_NONE);
- usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
-
- usart_enable(USART1);
-}
-
-int main(void)
-{
- clock_setup();
- gpio_setup();
- tick_setup();
- usart_setup();
-
- ds_init();
- for (;;) {
- gpio_toggle(GPIOC, GPIO13);
- delay_ms(100);
- ds_step();
- }
-
- return 0;
-}
+++ /dev/null
-#include "util.h"
-
-#include <libopencm3/stm32/usart.h>
-
-#include <stdarg.h>
-#include <string.h>
-
-#ifdef DEBUG_SEMIHOSTING
-
-void semi_put_char(char c)
-{
- // This is tricky, we need to work around GCC bugs
- volatile char cc = c;
- asm volatile (
- "mov r0, #0x03\n" /* SYS_WRITEC */
- "mov r1, %[msg]\n"
- "bkpt #0xAB\n"
- :
- : [msg] "r" (&cc)
- : "r0", "r1"
- );
-}
-
-void semi_write_string(char *c)
-{
- asm volatile (
- "mov r0, #0x04\n" /* SYS_WRITE0 */
- "mov r1, %[msg]\n"
- "bkpt #0xAB\n"
- :
- : [msg] "r" (c)
- : "r0", "r1"
- );
-}
-
-#endif
-
-void debug_putc(int c)
-{
-#ifdef DEBUG_SEMIHOSTING
- static char debug_buf[128];
- static int debug_i;
- debug_buf[debug_i++] = c;
- if (c == '\n' || debug_i >= sizeof(debug_buf) - 1) {
- debug_buf[debug_i] = 0;
- semi_write_string(debug_buf);
- debug_i = 0;
- }
-#endif
-#ifdef DEBUG_USART
- if (c == '\n')
- usart_send_blocking(USART1, '\r');
- usart_send_blocking(USART1, c);
-#endif
-}
-
-void debug_puts(const char *s)
-{
- while (*s)
- debug_putc(*s++);
-}
-
-enum printf_flags {
- PF_ZERO_PAD = 1,
- PF_SIGNED = 2,
- PF_NEGATIVE = 4,
- PF_UPPERCASE = 8,
- PF_LEFT = 16,
-};
-
-static void printf_string(const char *s, uint width, uint flags)
-{
- uint len = strlen(s);
- uint pad = (len < width) ? width - len : 0;
- char pad_char = (flags & PF_ZERO_PAD) ? '0' : ' ';
-
- if (flags & PF_LEFT)
- debug_puts(s);
- while (pad--)
- debug_putc(pad_char);
- if (!(flags & PF_LEFT))
- debug_puts(s);
-}
-
-static void printf_number(uint i, uint width, uint flags, uint base)
-{
- char buf[16];
- char *w = buf + sizeof(buf);
-
- if (flags & PF_SIGNED) {
- if ((int) i < 0) {
- i = - (int) i;
- flags |= PF_NEGATIVE;
- }
- }
-
- *--w = 0;
- do {
- uint digit = i % base;
- if (digit < 10)
- *--w = '0' + digit;
- else
- *--w = ((flags & PF_UPPERCASE) ? 'A' : 'a') + digit - 10;
- i /= base;
- }
- while (i);
-
- if (flags & PF_NEGATIVE)
- *--w = '-';
-
- printf_string(w, width, flags);
-}
-
-void debug_printf(const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
-
- while (*fmt) {
- int c = *fmt++;
- if (c != '%') {
- debug_putc(c);
- continue;
- }
-
- uint width = 0;
- uint flags = 0;
-
- if (*fmt == '-') {
- fmt++;
- flags |= PF_LEFT;
- }
-
- if (*fmt == '0') {
- fmt++;
- flags |= PF_ZERO_PAD;
- }
-
- while (*fmt >= '0' && *fmt <= '9')
- width = 10*width + *fmt++ - '0';
-
- c = *fmt++;
- switch (c) {
- case 'd':
- printf_number(va_arg(args, int), width, flags | PF_SIGNED, 10);
- break;
- case 'u':
- printf_number(va_arg(args, int), width, flags, 10);
- break;
- case 'X':
- flags |= PF_UPPERCASE;
- // fall-thru
- case 'x':
- printf_number(va_arg(args, int), width, flags, 16);
- break;
- case 's':
- printf_string(va_arg(args, char *), width, flags);
- break;
- default:
- debug_putc(c);
- continue;
- }
- }
-
- va_end(args);
-}
+++ /dev/null
-#include <stdint.h>
-#include <stdbool.h>
-
-typedef unsigned int uint;
-typedef uint8_t byte;
-typedef uint16_t u16;
-typedef int16_t s16;
-typedef uint32_t u32;
-typedef int32_t s32;
-
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-
-static inline uint get_u16_le(byte *p)
-{
- return (p[1] << 8) | p[0];
-}
-
-static inline uint get_u16_be(byte *p)
-{
- return (p[0] << 8) | p[1];
-}
-
-static inline uint get_u32_le(byte *p)
-{
- return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
-}
-
-static inline uint get_u32_be(byte *p)
-{
- return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-}
-
-static inline void put_u16_le(byte *p, u16 x)
-{
- p[0] = x;
- p[1] = x >> 8;
-}
-
-static inline void put_u16_be(byte *p, u16 x)
-{
- p[0] = x >> 8;
- p[1] = x;
-}
-
-static inline void put_u32_be(byte *p, u32 x)
-{
- p[0] = x >> 24;
- p[1] = (x >> 16) & 0xff;
- p[2] = (x >> 8) & 0xff;
- p[3] = x & 0xff;
-}
-
-static inline void put_u32_le(byte *p, u32 x)
-{
- p[3] = x >> 24;
- p[2] = (x >> 16) & 0xff;
- p[1] = (x >> 8) & 0xff;
- p[0] = x & 0xff;
-}
-
-// debug.c
-
-// #define DEBUG_SEMIHOSTING
-#define DEBUG_USART USART1
-
-void debug_printf(const char *fmt, ...);
-void debug_puts(const char *s);
-void debug_putc(int c);
--- /dev/null
+BINARY=test
+OBJS=test.o util-debug.o ds18b20.o
+
+OPENCM3_DIR=/home/mj/stm/libopencm3
+DEVICE=stm32f103x8
+
+all: $(BINARY).elf $(BINARY).bin
+
+flash: all
+ ../bin/st-flash write $(BINARY).bin 0x8000000
+
+reset: all
+ ../bin/st-flash reset
+
+ifneq ($(V),1)
+Q := @
+NULL := 2>/dev/null
+endif
+
+include $(OPENCM3_DIR)/mk/genlink-config.mk
+
+PREFIX ?= arm-none-eabi
+
+CC := $(PREFIX)-gcc
+CXX := $(PREFIX)-g++
+LD := $(PREFIX)-gcc
+AR := $(PREFIX)-ar
+AS := $(PREFIX)-as
+OBJCOPY := $(PREFIX)-objcopy
+OBJDUMP := $(PREFIX)-objdump
+GDB := $(PREFIX)-gdb
+OPT := -Os
+DEBUG := -ggdb3
+CSTD ?= -std=c99
+
+TGT_CFLAGS += $(OPT) $(CSTD) $(DEBUG)
+TGT_CFLAGS += $(ARCH_FLAGS)
+TGT_CFLAGS += -Wall -Wextra -Wshadow -Wimplicit-function-declaration
+TGT_CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
+TGT_CFLAGS += -fno-common -ffunction-sections -fdata-sections
+
+TGT_CPPFLAGS += -MD
+
+TGT_LDFLAGS += --static -nostartfiles
+TGT_LDFLAGS += -T$(LDSCRIPT)
+TGT_LDFLAGS += $(ARCH_FLAGS) $(DEBUG)
+TGT_LDFLAGS += -Wl,-Map=$(*).map -Wl,--cref
+TGT_LDFLAGS += -Wl,--gc-sections
+ifeq ($(V),99)
+TGT_LDFLAGS += -Wl,--print-gc-sections
+endif
+
+LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
+
+include $(OPENCM3_DIR)/mk/genlink-rules.mk
+
+%.bin: %.elf
+ @printf " OBJCOPY $(*).bin\n"
+ $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin
+
+%.elf: $(OBJS) $(LDSCRIPT)
+ @printf " LD $(*).elf\n"
+ $(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $*.elf
+
+%.o: %.c
+ @printf " CC $(*).c\n"
+ $(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $*.o -c $*.c
+
+.PHONY: clean
+clean:
+ @printf " CLEAN\n"
+ $(Q)rm -f *.elf *.bin *.o *.d *.map $(LDSCRIPT)
+
+%.stlink-flash: %.bin
+ @printf " FLASH $<\n"
+ $(STFLASH) write $(*).bin 0x8000000
+
+.SECONDEXPANSION:
+.SECONDARY:
+
+-include $(OBJS:.o=.d)
--- /dev/null
+// DS18B20 Temperature Sensors
+
+#include "util.h"
+#include "ds18b20.h"
+
+#include <libopencm3/cm3/cortex.h>
+#include <libopencm3/stm32/dma.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/timer.h>
+#include <string.h>
+
+static volatile u32 ds_dma_buffer;
+
+#define DS_TIMER TIM3
+#define DS_GPIO GPIOA
+#define DS_PIN GPIO7
+#define DS_DMA DMA1
+#define DS_DMA_CH 6
+
+#define DS_DEBUG
+#undef DS_DEBUG2
+
+#ifdef DS_DEBUG
+#define DEBUG debug_printf
+#else
+#define DEBUG(xxx, ...) do { } while (0)
+#endif
+
+#ifdef DS_DEBUG2
+#define DEBUG2 debug_printf
+#else
+#define DEBUG2(xxx, ...) do { } while (0)
+#endif
+
+// Current temperature
+int ds_current_temp = DS_TEMP_UNKNOWN;
+
+// Auxiliary functions which are missing from libopencm3
+static inline bool timer_is_counter_enabled(u32 timer)
+{
+ return TIM_CR1(timer) & TIM_CR1_CEN;
+}
+
+static inline void timer_enable_dma_cc1(u32 timer)
+{
+ TIM_DIER(timer) |= TIM_DIER_CC1DE;
+}
+
+static inline void timer_disable_dma_cc1(u32 timer)
+{
+ TIM_DIER(timer) &= ~TIM_DIER_CC1DE;
+}
+
+static bool ds_reset(void)
+{
+ DEBUG2("DS18B20: Reset\n");
+ timer_disable_counter(DS_TIMER);
+ timer_one_shot_mode(DS_TIMER);
+
+ // DMA for reading pin state
+ ds_dma_buffer = 0xdeadbeef;
+ dma_set_memory_address(DS_DMA, DS_DMA_CH, (u32) &ds_dma_buffer);
+ dma_set_peripheral_address(DS_DMA, DS_DMA_CH, (u32) &GPIO_IDR(DS_GPIO));
+ dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
+ dma_enable_channel(DS_DMA, DS_DMA_CH);
+
+ // CC1 is used to drive the DMA (read line state at specified time)
+ timer_disable_oc_output(DS_TIMER, TIM_OC1);
+ timer_set_oc_mode(DS_TIMER, TIM_OC1, TIM_OCM_FROZEN);
+ timer_set_oc_value(DS_TIMER, TIM_OC1, 560);
+ timer_set_dma_on_compare_event(DS_TIMER);
+ timer_enable_dma_cc1(DS_TIMER);
+
+ // CC2 is used to generate pulses (return line to idle state at specified time)
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
+ timer_enable_oc_output(DS_TIMER, TIM_OC2);
+ timer_set_oc_value(DS_TIMER, TIM_OC2, 480);
+ timer_set_oc_polarity_low(DS_TIMER, TIM_OC2);
+
+ // Set timer period to the length of the whole transaction (1 ms)
+ timer_set_period(DS_TIMER, 999);
+
+ // Pull line down and start timer
+ cm_disable_interrupts();
+ timer_enable_counter(DS_TIMER);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
+ cm_enable_interrupts();
+
+ // Wait until the timer expires
+ while (timer_is_counter_enabled(DS_TIMER))
+ ;
+ // Counter is automatically disabled at the end of cycle
+
+ // Disable DMA
+ timer_disable_dma_cc1(DS_TIMER);
+ dma_disable_channel(DS_DMA, DS_DMA_CH);
+
+ DEBUG2("Init DMA: %08x [%u] (%u remains)\n",
+ ds_dma_buffer,
+ !!(ds_dma_buffer & GPIO7),
+ dma_get_number_of_data(DS_DMA, DS_DMA_CH));
+
+ // Did the device respond?
+ if (ds_dma_buffer & GPIO7) {
+ DEBUG("DS18B20: Initialization failed\n");
+ return 0;
+ } else
+ return 1;
+}
+
+static void ds_send_bit(bool bit)
+{
+ timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
+ timer_set_oc_value(DS_TIMER, TIM_OC2, (bit ? 3 : 89)); // 1: 3μs pulse, 0: 89μs pulse
+ cm_disable_interrupts();
+ // XXX: On STM32F1, we must configure the OC channel _after_ we enable the counter,
+ // otherwise OC triggers immediately. Reasons?
+ timer_enable_counter(DS_TIMER);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
+ cm_enable_interrupts();
+ while (timer_is_counter_enabled(DS_TIMER))
+ ;
+}
+
+static void ds_send_byte(byte b)
+{
+ DEBUG2("DS write: %02x\n", b);
+ for (uint m = 1; m < 0x100; m <<= 1)
+ ds_send_bit(b & m);
+}
+
+static bool ds_recv_bit(void)
+{
+ timer_set_period(DS_TIMER, 79); // Each read slot takes 80μs
+ timer_set_oc_value(DS_TIMER, TIM_OC2, 2); // Generate 2μs pulse to start read slot
+ timer_set_oc_value(DS_TIMER, TIM_OC1, 8); // Sample data 8μs after start of slot
+ timer_enable_dma_cc1(DS_TIMER);
+
+ ds_dma_buffer = 0xdeadbeef;
+ dma_set_number_of_data(DS_DMA, DS_DMA_CH, 1);
+ dma_enable_channel(DS_DMA, DS_DMA_CH);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH);
+ cm_disable_interrupts();
+ timer_enable_counter(DS_TIMER);
+ timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_INACTIVE);
+ cm_enable_interrupts();
+ while (timer_is_counter_enabled(DS_TIMER))
+ ;
+ // DEBUG2("XXX %08x\n", ds_dma_buffer);
+ bool out = ds_dma_buffer & GPIO7;
+ dma_disable_channel(DS_DMA, DS_DMA_CH);
+
+ timer_disable_dma_cc1(DS_TIMER);
+
+ return out;
+}
+
+static byte ds_recv_byte(void)
+{
+ uint out = 0;
+ for (uint m = 1; m < 0x100; m <<= 1) {
+ if (ds_recv_bit())
+ out |= m;
+ }
+
+ DEBUG2("DS read: %02x\n", out);
+ return out;
+}
+
+static byte ds_buf[10];
+
+static byte ds_crc_block(uint n)
+{
+ /// XXX: This might be worth optimizing
+ uint crc = 0;
+
+ for (uint i = 0; i < n; i++) {
+ byte b = ds_buf[i];
+ for (uint j = 0; j < 8; j++) {
+ uint k = (b & 1) ^ (crc >> 7);
+ crc = (crc << 1) & 0xff;
+ if (k)
+ crc ^= 0x31;
+ b >>= 1;
+ }
+ }
+
+ return crc;
+}
+
+static bool ds_recv_block(uint n)
+{
+ for (uint i = 0; i < n; i++)
+ ds_buf[i] = ds_recv_byte();
+
+ byte crc = ds_crc_block(n);
+ if (crc) {
+ DEBUG("DS18B20: Invalid CRC %02x\n", crc);
+ return 0;
+ }
+ return 1;
+}
+
+struct ds_sensor ds_sensors[DS_NUM_SENSORS];
+
+#if DS_NUM_SENSORS == 1
+
+static void ds_enumerate(void)
+{
+ if (!ds_reset())
+ return;
+
+ ds_send_byte(0x33); // READ_ROM
+ if (!ds_recv_block(8))
+ return;
+
+ DEBUG("DS18B20: Found sensor ");
+ for (uint i = 0; i < 8; i++) {
+ DEBUG("%02x", ds_buf[i]);
+ ds_sensors[0].address[i] = ds_buf[i];
+ }
+ DEBUG("\n");
+}
+
+#else
+
+static void ds_enumerate(void)
+{
+ /*
+ * The enumeration algorithm roughly follows the one described in the
+ * Book of iButton Standards (Maxim Integrated Application Note 937).
+ *
+ * It simulates depth-first search on the trie of all device IDs.
+ * In each pass, it walks the trie from the root and recognizes branching nodes.
+ *
+ * The old_choice variable remembers the deepest left branch taken in the
+ * previous pass, new_choice is the same for the current pass.
+ */
+
+ DEBUG("DS18B20: Enumerate\n");
+
+ uint num_sensors = 0;
+ byte *addr = ds_buf;
+ byte old_choice = 0;
+
+ for (;;) {
+ if (!ds_reset()) {
+ DEBUG("DS18B20: Enumeration found no sensor\n");
+ return;
+ }
+
+ ds_send_byte(0xf0); // SEARCH_ROM
+ byte new_choice = 0;
+ for (byte i=0; i<64; i++) {
+ bool have_one = ds_recv_bit();
+ bool have_zero = ds_recv_bit();
+ bool old_bit = addr[i/8] & (1U << (i%8));
+ bool new_bit;
+ switch (2*have_one + have_zero) {
+ case 3:
+ // This should not happen
+ DEBUG("DS18B20: Enumeration failed\n");
+ return;
+ case 1:
+ // Only 0
+ new_bit = 0;
+ break;
+ case 2:
+ // Only 1
+ new_bit = 1;
+ break;
+ default:
+ // Both
+ if (i == old_choice)
+ new_bit = 1;
+ else if (i > old_choice) {
+ new_bit = 0;
+ new_choice = i;
+ } else {
+ new_bit = old_bit;
+ if (!new_bit)
+ new_choice = i;
+ }
+ }
+ if (new_bit)
+ addr[i/8] |= 1U << (i%8);
+ else
+ addr[i/8] &= ~(1U << (i%8));
+ ds_send_bit(new_bit);
+ }
+
+ if (num_sensors >= DS_NUM_SENSORS) {
+ DEBUG("DS18B20: Too many sensors\n");
+ return;
+ }
+
+ DEBUG("DS18B20: Found sensor #%u: ", num_sensors);
+ for (byte i=0; i<8; i++)
+ DEBUG("%02x", addr[i]);
+ if (ds_crc_block(8)) {
+ DEBUG(" - invalid CRC!\n");
+ } else if (ds_buf[0] == 0x28) {
+ DEBUG("\n");
+ memcpy(ds_sensors[num_sensors].address, ds_buf, 8);
+ num_sensors++;
+ } else {
+ DEBUG(" - wrong type\n");
+ }
+
+ old_choice = new_choice;
+ if (!old_choice)
+ break;
+ }
+}
+
+#endif
+
+void ds_init(void)
+{
+ DEBUG("DS18B20: Init\n");
+
+ for (uint i = 0; i < DS_NUM_SENSORS; i++) {
+ memset(ds_sensors[i].address, 0, 8);
+ ds_sensors[i].current_temp = DS_TEMP_UNKNOWN;
+ }
+
+ dma_set_read_from_peripheral(DS_DMA, DS_DMA_CH);
+ dma_set_priority(DS_DMA, DS_DMA_CH, DMA_CCR_PL_VERY_HIGH);
+ dma_disable_peripheral_increment_mode(DS_DMA, DS_DMA_CH);
+ dma_enable_memory_increment_mode(DS_DMA, DS_DMA_CH);
+ dma_set_peripheral_size(DS_DMA, DS_DMA_CH, DMA_CCR_PSIZE_16BIT);
+ dma_set_memory_size(DS_DMA, DS_DMA_CH, DMA_CCR_MSIZE_16BIT);
+
+ timer_set_prescaler(DS_TIMER, 71); // 1 tick = 1 μs
+ timer_set_mode(DS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+ timer_disable_preload(DS_TIMER);
+
+ gpio_set_mode(DS_GPIO, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7);
+
+ ds_enumerate();
+
+ // FIXME: Configure precision?
+}
+
+#if DS_NUM_SENSORS == 1
+#define ds_current_id 0
+#else
+ static byte ds_current_id;
+#endif
+
+static bool ds_activate(void)
+{
+ if (!ds_reset()) {
+ DEBUG("DS18B20: Reset failed\n");
+ return false;
+ }
+#if DS_NUM_SENSORS == 1
+ ds_send_byte(0xcc); // SKIP_ROM
+#else
+ ds_send_byte(0x55); // MATCH_ROM
+ for (uint i = 0; i < 8; i++)
+ ds_send_byte(ds_sensors[ds_current_id].address[i]);
+#endif
+ return true;
+}
+
+void ds_step(void)
+{
+ static byte ds_running;
+ static byte ds_timeout;
+
+ if (!ds_running) {
+ // Start measurement
+#if DS_NUM_SENSORS != 1
+ uint maxn = DS_NUM_SENSORS;
+ do {
+ if (!maxn--)
+ return;
+ ds_current_id++;
+ if (ds_current_id >= DS_NUM_SENSORS)
+ ds_current_id = 0;
+ } while (!ds_sensors[ds_current_id].address[0]);
+#endif
+ if (!ds_activate()) {
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
+ return;
+ }
+ ds_send_byte(0x44); // CONVERT_T
+ ds_running = 1;
+ ds_timeout = 255;
+ } else {
+ // Still running?
+ if (!ds_recv_bit()) {
+ if (!ds_timeout--) {
+ DEBUG("DS18B20 #%u: Timeout\n", ds_current_id);
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
+ ds_running = 0;
+ }
+ return;
+ }
+ ds_running = 0;
+
+ // Read scratch pad
+ if (!ds_activate())
+ return;
+ ds_send_byte(0xbe); // READ_SCRATCHPAD
+ if (!ds_recv_block(9)) {
+ ds_sensors[ds_current_id].current_temp = DS_TEMP_UNKNOWN;
+ return;
+ }
+ int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8));
+ t = t * 1000 / 16;
+
+ DEBUG("DS18B20 #%u: %d.%03d degC\n", ds_current_id, t / 1000, t % 1000);
+ ds_sensors[ds_current_id].current_temp = t;
+ }
+}
--- /dev/null
+// DS18B20 Temperature Sensors
+
+#ifndef _DS18B20_H
+#define _DS18B20_H
+
+// Maximum number of supported sensors
+#define DS_NUM_SENSORS 8
+
+struct ds_sensor {
+ byte address[8]; // All zeroes if sensor does not exist.
+ // Otherwise, address[0] is guaranteed to be non-zero.
+ int current_temp; // Temperature in m°C or DS_TEMP_UNKNOWN
+};
+
+extern struct ds_sensor ds_sensors[DS_NUM_SENSORS];
+
+#define DS_TEMP_UNKNOWN 0x7fffffff
+
+void ds_init(void);
+void ds_step(void);
+
+#endif
--- /dev/null
+#include "util.h"
+#include "ds18b20.h"
+
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/usart.h>
+
+static void clock_setup(void)
+{
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_USART1);
+ rcc_periph_clock_enable(RCC_TIM3);
+ rcc_periph_clock_enable(RCC_DMA1);
+}
+
+static void gpio_setup(void)
+{
+ gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
+}
+
+static volatile u32 ms_ticks;
+
+void sys_tick_handler(void)
+{
+ ms_ticks++;
+}
+
+static void tick_setup(void)
+{
+ systick_set_frequency(1000, 72000000);
+ systick_counter_enable();
+ systick_interrupt_enable();
+}
+
+static void delay_ms(uint ms)
+{
+ u32 start_ticks = ms_ticks;
+ while (ms_ticks - start_ticks < ms)
+ ;
+}
+
+static void usart_setup(void)
+{
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
+
+ usart_set_baudrate(USART1, 115200);
+ usart_set_databits(USART1, 8);
+ usart_set_stopbits(USART1, USART_STOPBITS_1);
+ usart_set_mode(USART1, USART_MODE_TX);
+ usart_set_parity(USART1, USART_PARITY_NONE);
+ usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+
+ usart_enable(USART1);
+}
+
+int main(void)
+{
+ clock_setup();
+ gpio_setup();
+ tick_setup();
+ usart_setup();
+
+ ds_init();
+ for (;;) {
+ gpio_toggle(GPIOC, GPIO13);
+ delay_ms(100);
+ ds_step();
+ }
+
+ return 0;
+}
--- /dev/null
+#include "util.h"
+
+#include <libopencm3/stm32/usart.h>
+
+#include <stdarg.h>
+#include <string.h>
+
+#ifdef DEBUG_SEMIHOSTING
+
+void semi_put_char(char c)
+{
+ // This is tricky, we need to work around GCC bugs
+ volatile char cc = c;
+ asm volatile (
+ "mov r0, #0x03\n" /* SYS_WRITEC */
+ "mov r1, %[msg]\n"
+ "bkpt #0xAB\n"
+ :
+ : [msg] "r" (&cc)
+ : "r0", "r1"
+ );
+}
+
+void semi_write_string(char *c)
+{
+ asm volatile (
+ "mov r0, #0x04\n" /* SYS_WRITE0 */
+ "mov r1, %[msg]\n"
+ "bkpt #0xAB\n"
+ :
+ : [msg] "r" (c)
+ : "r0", "r1"
+ );
+}
+
+#endif
+
+void debug_putc(int c)
+{
+#ifdef DEBUG_SEMIHOSTING
+ static char debug_buf[128];
+ static int debug_i;
+ debug_buf[debug_i++] = c;
+ if (c == '\n' || debug_i >= sizeof(debug_buf) - 1) {
+ debug_buf[debug_i] = 0;
+ semi_write_string(debug_buf);
+ debug_i = 0;
+ }
+#endif
+#ifdef DEBUG_USART
+ if (c == '\n')
+ usart_send_blocking(USART1, '\r');
+ usart_send_blocking(USART1, c);
+#endif
+}
+
+void debug_puts(const char *s)
+{
+ while (*s)
+ debug_putc(*s++);
+}
+
+enum printf_flags {
+ PF_ZERO_PAD = 1,
+ PF_SIGNED = 2,
+ PF_NEGATIVE = 4,
+ PF_UPPERCASE = 8,
+ PF_LEFT = 16,
+};
+
+static void printf_string(const char *s, uint width, uint flags)
+{
+ uint len = strlen(s);
+ uint pad = (len < width) ? width - len : 0;
+ char pad_char = (flags & PF_ZERO_PAD) ? '0' : ' ';
+
+ if (flags & PF_LEFT)
+ debug_puts(s);
+ while (pad--)
+ debug_putc(pad_char);
+ if (!(flags & PF_LEFT))
+ debug_puts(s);
+}
+
+static void printf_number(uint i, uint width, uint flags, uint base)
+{
+ char buf[16];
+ char *w = buf + sizeof(buf);
+
+ if (flags & PF_SIGNED) {
+ if ((int) i < 0) {
+ i = - (int) i;
+ flags |= PF_NEGATIVE;
+ }
+ }
+
+ *--w = 0;
+ do {
+ uint digit = i % base;
+ if (digit < 10)
+ *--w = '0' + digit;
+ else
+ *--w = ((flags & PF_UPPERCASE) ? 'A' : 'a') + digit - 10;
+ i /= base;
+ }
+ while (i);
+
+ if (flags & PF_NEGATIVE)
+ *--w = '-';
+
+ printf_string(w, width, flags);
+}
+
+void debug_printf(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ while (*fmt) {
+ int c = *fmt++;
+ if (c != '%') {
+ debug_putc(c);
+ continue;
+ }
+
+ uint width = 0;
+ uint flags = 0;
+
+ if (*fmt == '-') {
+ fmt++;
+ flags |= PF_LEFT;
+ }
+
+ if (*fmt == '0') {
+ fmt++;
+ flags |= PF_ZERO_PAD;
+ }
+
+ while (*fmt >= '0' && *fmt <= '9')
+ width = 10*width + *fmt++ - '0';
+
+ c = *fmt++;
+ switch (c) {
+ case 'd':
+ printf_number(va_arg(args, int), width, flags | PF_SIGNED, 10);
+ break;
+ case 'u':
+ printf_number(va_arg(args, int), width, flags, 10);
+ break;
+ case 'X':
+ flags |= PF_UPPERCASE;
+ // fall-thru
+ case 'x':
+ printf_number(va_arg(args, int), width, flags, 16);
+ break;
+ case 's':
+ printf_string(va_arg(args, char *), width, flags);
+ break;
+ default:
+ debug_putc(c);
+ continue;
+ }
+ }
+
+ va_end(args);
+}
--- /dev/null
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef unsigned int uint;
+typedef uint8_t byte;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+static inline uint get_u16_le(byte *p)
+{
+ return (p[1] << 8) | p[0];
+}
+
+static inline uint get_u16_be(byte *p)
+{
+ return (p[0] << 8) | p[1];
+}
+
+static inline uint get_u32_le(byte *p)
+{
+ return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
+}
+
+static inline uint get_u32_be(byte *p)
+{
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+static inline void put_u16_le(byte *p, u16 x)
+{
+ p[0] = x;
+ p[1] = x >> 8;
+}
+
+static inline void put_u16_be(byte *p, u16 x)
+{
+ p[0] = x >> 8;
+ p[1] = x;
+}
+
+static inline void put_u32_be(byte *p, u32 x)
+{
+ p[0] = x >> 24;
+ p[1] = (x >> 16) & 0xff;
+ p[2] = (x >> 8) & 0xff;
+ p[3] = x & 0xff;
+}
+
+static inline void put_u32_le(byte *p, u32 x)
+{
+ p[3] = x >> 24;
+ p[2] = (x >> 16) & 0xff;
+ p[1] = (x >> 8) & 0xff;
+ p[0] = x & 0xff;
+}
+
+// debug.c
+
+// #define DEBUG_SEMIHOSTING
+#define DEBUG_USART USART1
+
+void debug_printf(const char *fmt, ...);
+void debug_puts(const char *s);
+void debug_putc(int c);