]> mj.ucw.cz Git - home-hw.git/commitdiff
ocm3 test: DS18B20 interface
authorMartin Mares <mj@ucw.cz>
Mon, 1 Jul 2019 08:19:22 +0000 (10:19 +0200)
committerMartin Mares <mj@ucw.cz>
Mon, 1 Jul 2019 08:19:22 +0000 (10:19 +0200)
test-opencm3/Makefile
test-opencm3/ds18b20.c [new file with mode: 0644]
test-opencm3/ds18b20.h [new file with mode: 0644]
test-opencm3/kerm [new file with mode: 0644]
test-opencm3/test.c

index afae34600f9a3894d27610d661cbe945a589ceda..89f989794dd04593404b4efd931b022b69494b0b 100644 (file)
@@ -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 (file)
index 0000000..387a88c
--- /dev/null
@@ -0,0 +1,256 @@
+// DS18B20 Temperature Sensor
+
+#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>
+
+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 (file)
index 0000000..696e7bc
--- /dev/null
@@ -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 (file)
index 0000000..988f567
--- /dev/null
@@ -0,0 +1,5 @@
+set port /dev/ttyUSB0
+set speed 115200
+set flow-control none
+set carrier-watch off
+connect
index f02195d232f84c1e31e8d33e5ceb5236efdfabba..bd73c870246a717e2b310fdf27622efbc21b8840 100644 (file)
@@ -1,4 +1,5 @@
 #include "util.h"
+#include "ds18b20.h"
 
 #include <libopencm3/cm3/nvic.h>
 #include <libopencm3/cm3/systick.h>
@@ -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;