From: Martin Mares Date: Wed, 3 Jul 2019 22:10:08 +0000 (+0200) Subject: A simple program for testing of DS18B20 sensors X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=f61cccedbec417684b880627b4c5a82a1c4038ef;p=home-hw.git A simple program for testing of DS18B20 sensors --- diff --git a/ds-test/Makefile b/ds-test/Makefile new file mode 100644 index 0000000..3e3438f --- /dev/null +++ b/ds-test/Makefile @@ -0,0 +1,81 @@ +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) diff --git a/ds-test/ds18b20.c b/ds-test/ds18b20.c new file mode 100644 index 0000000..53df2a6 --- /dev/null +++ b/ds-test/ds18b20.c @@ -0,0 +1,419 @@ +// DS18B20 Temperature Sensors + +#include "util.h" +#include "ds18b20.h" + +#include +#include +#include +#include +#include +#include + +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; + } +} diff --git a/ds-test/ds18b20.h b/ds-test/ds18b20.h new file mode 100644 index 0000000..6ca5d75 --- /dev/null +++ b/ds-test/ds18b20.h @@ -0,0 +1,22 @@ +// 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 diff --git a/ds-test/test.c b/ds-test/test.c new file mode 100644 index 0000000..309fc75 --- /dev/null +++ b/ds-test/test.c @@ -0,0 +1,76 @@ +#include "util.h" +#include "ds18b20.h" + +#include +#include +#include +#include +#include + +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; +} diff --git a/ds-test/util-debug.c b/ds-test/util-debug.c new file mode 100644 index 0000000..a1698cf --- /dev/null +++ b/ds-test/util-debug.c @@ -0,0 +1,166 @@ +#include "util.h" + +#include + +#include +#include + +#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); +} diff --git a/ds-test/util.h b/ds-test/util.h new file mode 100644 index 0000000..6d8c9cc --- /dev/null +++ b/ds-test/util.h @@ -0,0 +1,69 @@ +#include +#include + +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);