/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
*
* $LOG$
- * 2019/8/4 - ROBIN KRENS
+ * 2019/8/28 - ROBIN KRENS
* Initial version
*
* $DESCRIPTION$
- * Temperature sensor
+ * DS18B20 temperature sensor implementation
+ * Uses the 1-wire protocol. You will need to setup the temperature sensor
+ * with an external pull up resistor. High is the idle state of the chip.
+ * This implementation does not use parasite power. Uses busy waits.
+ * The chip doesn't require too accurate timings. Busy waits are between 15
+ * and 240 microseconds.
+ *
+ * The temperature conversion on the chip it takes a lot longer (up to 700ms).
+ * In case temperature is often read, implementing this with interrupts might be
+ * worthwile.
+ *
+ * Each series of commands is initiated with a long reset and prescence pulse.
+ * Implemented:
+ * - Read ID of Chip
+ * - Convert and read temperature
+ *
+ * TODO:
+ * - Scratchpad copy functionality (+EEPROM)
+ * - Alarm functionality
*
* */
#include <drivers/tsensor.h>
-#define PRESCALER 36000 // 1 kHz
+/* Commands */
+#define READ_ROM 0x33
+#define SKIP_ROM 0xCC
+#define READ_SCRATCH 0xBE
+#define CONVERT_T 0x44
- void * update_handler() {
+/* Basic GPIO settings for output pulses and input */
+static void in_conf() {
+ rwrite(GPIOB_CRL, 0x44444444);
+}
- if(rchkbit(TIM4_SR1, 1)) {
+static void out_conf() {
+ rwrite(GPIOB_CRL, 0x46444444); // open drain (with external pullup resistor)
+}
- printf("RISING EDGE CAUGHT\n");
- printf("CCR1: %p\n", *TIM4_CCR1);
+/* Send command cmd over data wire. Each write slot should be
+ * at least 60ms. */
+static void send_cmd(unsigned char cmd) {
+
+ int pos = 0;
+
+ for (int i = 0; i < 8; i++) {
+ // initiate write slot
+ out_conf();
+ rclrbit(GPIOB_ODR, 6); // pull low
+
+ // writing a logical 1 or 0
+ if ((cmd >> pos) & 0x01) {
+ _block(5);
+ in_conf();
+ _block(60);
+ }
+ else {
+ _block(60);
+ in_conf();
+ }
+ pos++;
}
-
- if(rchkbit(TIM4_SR1, 2)) {
- printf("FALLING EDGE CAUGHT\n");
- printf("CCR2: %p\n", *TIM4_CCR2);
+}
+
+/* Read reply of sensor. Depending on the command, the sensor
+ * can send back up to 9 bytes */
+static char get_byte() {
+
+ char c = 0x00;
+
+ for (int i = 0; i < 8; i++) {
+ /* Initate write slot*/
+ out_conf();
+ rclrbit(GPIOB_ODR, 6);
+ _block(3);
+ /* Listen for reply */
+ in_conf();
+ if (rchkbit(GPIOB_IDR,6)) {
+ c = c | (0x1 << i);
+ }
+ else {
+ c = c | (0x0 << i);
+ }
+ /* Each read slot should be at least 60 microseconds
+ * before the next one is initiated */
+ _block(60);
}
-
+ return c;
+}
- rclrbit(TIM4_SR1, 1);
- rclrbit(TIM4_SR1, 0);
- rclrbit(TIM4_SR1, 2); //
- rclrbit(TIM4_SR1, 9); // OF
- rclrbit(TIM4_SR1, 10); // OF
- rclrbit(TIM4_SR1, 6);
- // TODO clear overflow tag
-}
-void tsensor_output(uint16_t preload, uint16_t compare/*, uint16_t pulses */) {
+/* Initiate the sensor, send a reset pulse and wait consequently for
+ * presence pulse. Slots should be at least 450 and 240 microseconds.
+ * */
+static int tsensor_init() {
- /* GPIO AND CLOCK */
rsetbit(RCC_APB2ENR, 3); // GPIOB enable
- rwrite(GPIOB_CRL, 0x4A444444); // PB6 for Channel 1 TIM4 alternate
- rsetbit(RCC_APB1ENR, 2); // TIM4 enable
-
- rsetbitsfrom(TIM4_CR1, 5, 0x00); // edge-aligned mode
- rclrbit(TIM4_CR1, 4); // upcounter (clrbit! not needed to set)
-
- rwrite(TIM4_PSC, PRESCALER - 1); // 1 MHz
- rwrite(TIM4_ARR, preload); // preload
- rwrite(TIM4_CCR1, compare); // compare
- //rwrite(TIM4_RCR, pulses - 1); /* repeat ONLY IN ADVANCED TIMER */
- rsetbit(TIM4_EGR, 0); // update generation
-
- rsetbit(TIM4_CR1, 3); // one pulse mode
- rsetbitsfrom(TIM4_CCMR1, 4, 0x7); // PWM mode 1
+ /* send presence pulse */
+ out_conf();
+ rclrbit(GPIOB_ODR, 6); // pull low
+ _block(450);
+ /* wait for the chips reply */
+ in_conf();
+ _block(60);
+ if (!rchkbit(GPIOB_IDR, 6)) {
+ //printf("Info: sensor detected\n");
+ //get_id();
+ }
+ else {
+ printf("Error: no temperature sensor found!");
+ return -1;
+ }
+ _block(240); // finish intialization slot
- //rsetbit(TIM4_CCMR1, 3); // preload enable
- //rsetbit(TIM4_CR1, 7); // buffered
+ return 0;
+
+}
+
- rsetbit(TIM4_CCER, 0); // enable output channel 1
- rsetbit(TIM4_CR1, 0); // start counter
- /* INTERRUPTS */
- //ivt_set_gate(41, update_handler, 0);
+/* Wait for the chip to convert the temperature */
+static void wait_tconvert() {
- //rsetbit(TIM4_DIER, 0);
- //rsetbit(NVIC_ISER0, 25); // interupt 41 - 32
+ printf("Info: Converting temp\n");
+ /* initiate write slot */
+ out_conf();
+ rclrbit(GPIOB_ODR, 6);
+ _block(3);
+ in_conf();
+ while (!rchkbit(GPIOB_IDR, 6)) {
+ //printf(".");
+ _block(60);
+ out_conf();
+ rclrbit(GPIOB_ODR, 6);
+ _block(3);
+ in_conf();
+ }
}
-void tsensor_input(uint16_t preload) {
+/* Print some serial and CRC information about the chip */
+void tsensor_printid() {
- uint16_t timestamp;
- /* GPIO AND CLOCK */
- rsetbit(RCC_APB2ENR, 3); // GPIOB enable
- rwrite(GPIOB_CRL, 0x44444444); // Input floating (default state)
- rsetbit(RCC_APB1ENR, 2); // TIM4 enable
-
- //rsetbitsfrom(TIM4_CR1, 5, 0x00); // edge-aligned mode
- //rclrbit(TIM4_CR1, 4); // upcounter (clrbit! not needed to set)
+ tsensor_init();
+ send_cmd(READ_ROM);
- rwrite(TIM4_PSC, PRESCALER - 1); // 1 MHz
- rwrite(TIM4_ARR, preload); // preload
+ // replies with 8 bytes
+ int nbytes = 8;
+
+ char scratchbuf[nbytes];
+ memset(&scratchbuf, 0, sizeof(char) * nbytes);
+ scratchbuf[nbytes+1] = '\n';
- rsetbit(TIM4_CCMR1, 0); // input on TI1
- rsetbit(TIM4_CCMR1, 9); // another input TI2
- rsetbit(TIM4_CCER, 5); // other polarity, inverted
+ for (int i = 0; i < nbytes; i++) {
+ scratchbuf[i] = get_byte();
+ }
- /* TODO: reg funct */
- rsetbit(TIM4_SMCR, 4); // 101
- rsetbit(TIM4_SMCR, 6); // 101
+ printf("Family Code: %#x\n", scratchbuf[0]);
+ printf("Serial Number: 0x");
+ for (int i = 1; i < 7; i++) {
+ printf("%x", scratchbuf[i]);
+ }
+ printf("\n");
+ printf("CRC Code: %#x\n", scratchbuf[7]);
+
+}
+
+/* Convert and read temperature of chip. Sensor has to be initialized twice
+ * since there are two series of commands (see datasheet flowchart) */
+uint16_t tsensor_get_temp() {
+
+ /* initialize sensor and send commands */
+ tsensor_init();
+ send_cmd(SKIP_ROM);
+ send_cmd(CONVERT_T);
+ wait_tconvert();
+ tsensor_init();
+ send_cmd(SKIP_ROM);
+ send_cmd(READ_SCRATCH);
- // rsetbit(TIM4_SMCR, 2); // RESET rising edge triggers counter and generates update
- rsetbit(TIM4_SMCR, 2); // 110
- rsetbit(TIM4_SMCR, 1); // 110
+ /* Scratchpad is 9 bytes, but we are only interested in
+ * the first two bytes, since they contain the LSB and
+ * MSB of temperature */
+ int nbytes = 2;
- rsetbit(TIM4_CR1, 3); // one pulse mode // NOTE: RESET after finised preload
- // will catch multiple signal... can set fram
+ char scratchbuf[nbytes];
+ memset(&scratchbuf, 0, sizeof(char) * nbytes);
+ scratchbuf[nbytes+1] = '\n';
- rsetbit(TIM4_CCER, 0); // enable capture channel 1 (changed pos)
- rsetbit(TIM4_CCER, 4); // enable capture channel 2
- /* Caught on rising edge, no need to change*/
- /* Clear capture event flag */
-// rsetbit(TIM4_CR1, 0); // RESET with no trigger mode start
-
- // enable capture channel 1 interrupt
- rsetbit(TIM4_DIER, 1);
- rsetbit(TIM4_DIER, 2);
- ivt_set_gate(46, update_handler, 0);
- rsetbit(NVIC_ISER0, 30);
+ for (int i = 0; i < nbytes; i++) {
+ scratchbuf[i] = get_byte();
+ }
+ // LSB first four bits are after floating point
+ // uint8_t lsb_afltp = scratchbuf[0] & 0xF;
+
+ int8_t lsb_bfltp = (scratchbuf[0] >> 4) & 0xF;
+ // MSB only the first three bits are used
+ uint8_t msb = scratchbuf[1] & 0x7;
+
+ return (msb << 4) + lsb_bfltp;
+
}
+
+/*
+void test() {
+
+ //get_id();
+ uint16_t temp = get_temp();
+ printf("Current temperature: %d\n", temp);
+
+} */