--- /dev/null
+// 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
#include "util.h"
+#include "ds18b20.h"
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>
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)
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)
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)
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;