From 0b08538afd0220a97f1059828a4d8ee23504c9e9 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 1 Jul 2019 10:19:22 +0200 Subject: [PATCH] ocm3 test: DS18B20 interface --- test-opencm3/Makefile | 2 +- test-opencm3/ds18b20.c | 256 +++++++++++++++++++++++++++++++++++++++++ test-opencm3/ds18b20.h | 12 ++ test-opencm3/kerm | 5 + test-opencm3/test.c | 28 ++--- 5 files changed, 288 insertions(+), 15 deletions(-) create mode 100644 test-opencm3/ds18b20.c create mode 100644 test-opencm3/ds18b20.h create mode 100644 test-opencm3/kerm diff --git a/test-opencm3/Makefile b/test-opencm3/Makefile index afae346..89f9897 100644 --- a/test-opencm3/Makefile +++ b/test-opencm3/Makefile @@ -1,5 +1,5 @@ BINARY=test -OBJS=test.o util-debug.o +OBJS=test.o util-debug.o ds18b20.o OPENCM3_DIR=/home/mj/stm/libopencm3 DEVICE=stm32f103x8 diff --git a/test-opencm3/ds18b20.c b/test-opencm3/ds18b20.c new file mode 100644 index 0000000..387a88c --- /dev/null +++ b/test-opencm3/ds18b20.c @@ -0,0 +1,256 @@ +// DS18B20 Temperature Sensor + +#include "util.h" +#include "ds18b20.h" + +#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 +#define 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_byte(byte b) +{ + DEBUG2("DS write: %02x\n", b); + timer_set_period(DS_TIMER, 99); // Each write slot takes 100 μs + for (uint m = 1; m < 0x100; m <<= 1) { + timer_set_oc_mode(DS_TIMER, TIM_OC2, TIM_OCM_FORCE_HIGH); + timer_set_oc_value(DS_TIMER, TIM_OC2, ((b & m) ? 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 byte ds_recv_byte(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); + + uint out = 0; + for (uint m = 1; m < 0x100; m <<= 1) { + 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); + if (ds_dma_buffer & GPIO7) + out |= m; + dma_disable_channel(DS_DMA, DS_DMA_CH); + } + + timer_disable_dma_cc1(DS_TIMER); + DEBUG2("DS read: %02x\n", out); + return out; +} + +static byte ds_buf[10]; + +static bool ds_recv_block(uint n) +{ + uint crc = 0; + for (uint i = 0; i < n; i++) { + uint b = ds_recv_byte(); + // DEBUG("%02x ", b); + ds_buf[i] = b; + for (uint j = 0; j < 8; j++) { + uint k = (b & 1) ^ (crc >> 7); + crc = (crc << 1) & 0xff; + if (k) + crc ^= 0x31; + b >>= 1; + } + } + + if (crc) { + DEBUG("DS18B20: Invalid CRC %02x\n", crc); + return 0; + } + return 1; +} + +void ds_init(void) +{ + DEBUG("DS18B20: Init\n"); + + 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); + + // Identify device + if (!ds_reset()) + return; + ds_send_byte(0x33); + ds_recv_block(8); + + // FIXME: Configure precision +} + +#if 0 +void ds_step(void) +{ + static byte ds_running; + static byte ds_timeout; + + if (!ds_running) { + // Start measurement + if (!ds_reset()) { + ds_current_temp = DS_TEMP_UNKNOWN; + return; + } + ds_send_byte(0xcc); + ds_send_byte(0x44); + ds_running = 1; + ds_timeout = 255; + } else { + // Still running? + if (ds_recv_byte() != 0xff) { + if (!ds_timeout--) { + ds_current_temp = DS_TEMP_UNKNOWN; + ds_running = 0; + DEBUG("DS18B20: Timeout\n"); + } + return; + } + ds_running = 0; + + // Read scratch pad + if (!ds_reset()) { + ds_current_temp = DS_TEMP_UNKNOWN; + return; + } + ds_send_byte(0xcc); + ds_send_byte(0xbe); + if (!ds_recv_block(9)) { + ds_current_temp = DS_TEMP_UNKNOWN; + return; + } + int t = (int16_t) (ds_buf[0] | (ds_buf[1] << 8)); + t = t * 1000 / 16; + + DEBUG("DS18B20: %d.%03d degC\n", t / 1000, t % 1000); + ds_current_temp = t; + } +} +#endif diff --git a/test-opencm3/ds18b20.h b/test-opencm3/ds18b20.h new file mode 100644 index 0000000..696e7bc --- /dev/null +++ b/test-opencm3/ds18b20.h @@ -0,0 +1,12 @@ +// DS18B20 Temperature Sensor + +#ifndef _DS18B20_H +#define _DS18B20_H + +extern int ds_current_temp; +#define DS_TEMP_UNKNOWN 0x7fffffff + +void ds_init(void); +void ds_step(void); + +#endif diff --git a/test-opencm3/kerm b/test-opencm3/kerm new file mode 100644 index 0000000..988f567 --- /dev/null +++ b/test-opencm3/kerm @@ -0,0 +1,5 @@ +set port /dev/ttyUSB0 +set speed 115200 +set flow-control none +set carrier-watch off +connect diff --git a/test-opencm3/test.c b/test-opencm3/test.c index f02195d..bd73c87 100644 --- a/test-opencm3/test.c +++ b/test-opencm3/test.c @@ -1,4 +1,5 @@ #include "util.h" +#include "ds18b20.h" #include #include @@ -13,6 +14,8 @@ static void clock_setup(void) 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) @@ -34,7 +37,7 @@ static void tick_setup(void) systick_interrupt_enable(); } -static void delay_ms(uint ms) +void delay_ms(uint ms) { u32 start_ticks = ms_ticks; while (ms_ticks - start_ticks < ms) @@ -43,16 +46,16 @@ static void delay_ms(uint ms) static void usart_setup(void) { - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); + 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_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); + usart_enable(USART1); } int main(void) @@ -62,13 +65,10 @@ int main(void) tick_setup(); usart_setup(); - int cc = 32; for (;;) { gpio_toggle(GPIOC, GPIO13); - debug_putc(cc); - cc++; - if (cc >= 126) cc = 32; - delay_ms(100); + ds_init(); + delay_ms(1000); } return 0; -- 2.39.5