SHARE
TWEET

am2302.c

a guest Jan 22nd, 2013 1,409 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* DHT22 / AM2302 driver */
  2. #include <stdbool.h>
  3. #include "stm32f10x.h"
  4.  
  5. #include "FreeRTOS.h"
  6. #include "task.h"
  7. #include "queue.h"
  8. #include "am2302.h"
  9.  
  10. #define DHT_BIT_TIMEOUT_US (80 * 4) /* one bit timeout */
  11. #define DHT_START_PULSE_MS 2
  12. #define DHT_IRQ_TIMEOUT_MS 2 /* irq timeout */
  13. #define DHT_PKT_SIZE 5
  14. #define DHT_PKT_TIMEOUT_MS 10
  15.  
  16. /*-----------------------------------------------------------------------------*/
  17. /* Use TIM3_CH1 */
  18. #define DHT_GPIO GPIOC
  19. #define DHT_PIN GPIO_Pin_6
  20. #define DHT_CLK_ENABLE \
  21. do { \
  22.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); \
  23.         RCC_APB2PeriphClockCmd((RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO), ENABLE); \
  24. } while(0)
  25. #define DHT_TIMER TIM3 /* Uses APB2 clock */
  26. #define DHT_TIMER_CHANNEL TIM_Channel_1
  27. #define DHT_IRQN TIM3_IRQn
  28. #define DHT_GPIO_REMAP GPIO_FullRemap_TIM3
  29. #define DHT_IRQ_HANDLER TIM3_IRQHandler
  30.  
  31. #define DHT_GET_PCLK_FREQ(x) (((RCC->CFGR >> 8) & 0x7) >= 4 ? (x)->PCLK1_Frequency * 2 : (x)->PCLK1_Frequency)
  32.  
  33. #define DHT_IRQ_PRIO configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
  34. #define DHT_PRIO (configMAX_PRIORITIES - 1)
  35.  
  36. #define ERROR_DIV 2
  37. #define PERIOD_OK(x, l, h) \
  38.         ((x)->low >= ((l) - (l) / ERROR_DIV) && \
  39.         (x)->low < ((l) + (l) / ERROR_DIV) && \
  40.         ((x)->period - (x)->low) >= ((h) - (h) / ERROR_DIV) && \
  41.         ((x)->period - (x)->low) < ((h) + (h) / ERROR_DIV))
  42. /*-----------------------------------------------------------------------------*/
  43.  
  44. typedef struct _pwm_capture_t pwm_capture_t;
  45. typedef struct _dht_read_t dht_read_t;
  46.  
  47. struct _pwm_capture_t {
  48.         unsigned int period;
  49.         unsigned int low;
  50. };
  51.  
  52. struct _dht_read_t {
  53.         xSemaphoreHandle sem; /* in */
  54.         dht_error_t error; /* out */
  55.         uint8_t data[DHT_PKT_SIZE]; /* out */
  56. };
  57.  
  58. static void dht_thread(void *data);
  59. static inline void start_timer();
  60. static inline void stop_timer();
  61. /*-----------------------------------------------------------------------------*/
  62. static xQueueHandle cmd_msgbox; /* read requests */
  63. static xSemaphoreHandle irq_sem;
  64. static volatile pwm_capture_t pwm_data;
  65. /*-----------------------------------------------------------------------------*/
  66.  
  67. static void gpio_input() {
  68.         /* Pin configuration: input floating */
  69.         GPIO_InitTypeDef gpconf = {
  70.                 .GPIO_Pin = DHT_PIN,
  71.                 .GPIO_Mode = GPIO_Mode_IN_FLOATING,
  72.                 .GPIO_Speed = GPIO_Speed_50MHz,
  73.         };
  74.         GPIO_Init(DHT_GPIO, &gpconf);
  75. }
  76.  
  77. static void gpio_output() {
  78.         /* Pin configuration: output open-drain */
  79.         GPIO_InitTypeDef gpconf = {
  80.                 .GPIO_Pin = DHT_PIN,
  81.                 .GPIO_Mode = GPIO_Mode_Out_OD,
  82.                 .GPIO_Speed = GPIO_Speed_50MHz,
  83.         };
  84.         GPIO_Init(DHT_GPIO, &gpconf);
  85. }
  86.  
  87. int dht_init() {
  88.         if((cmd_msgbox = xQueueCreate(1, sizeof(dht_read_t*))) == NULL) return -1;
  89.         vSemaphoreCreateBinary(irq_sem);
  90.         if(irq_sem == NULL) return -1;
  91.         xSemaphoreTake(irq_sem, 0);
  92.  
  93.         xTaskCreate(dht_thread, (const signed char *)"DHT", configMINIMAL_STACK_SIZE, NULL, DHT_PRIO, NULL);
  94.  
  95.         /* Enable clocks */
  96.         DHT_CLK_ENABLE;
  97.  
  98.         /* Pin configuration */
  99.         gpio_input();
  100.  
  101. #ifdef DHT_GPIO_REMAP
  102.         GPIO_PinRemapConfig(DHT_GPIO_REMAP, ENABLE);
  103. #endif
  104.  
  105.         /* Enable the TIM global Interrupt */
  106.         NVIC_InitTypeDef itconf = {
  107.                 .NVIC_IRQChannel = DHT_IRQN,
  108.                 .NVIC_IRQChannelPreemptionPriority = DHT_IRQ_PRIO,
  109.                 .NVIC_IRQChannelSubPriority = 0,
  110.                 .NVIC_IRQChannelCmd = ENABLE,
  111.         };
  112.         NVIC_Init(&itconf);
  113.  
  114.         /* Give 1us resolution */
  115.         RCC_ClocksTypeDef clocks;
  116.         RCC_GetClocksFreq(&clocks);
  117.         unsigned long freq = DHT_GET_PCLK_FREQ(&clocks);
  118.  
  119.         TIM_PrescalerConfig(DHT_TIMER, freq / 1000000 - 1, TIM_PSCReloadMode_Immediate);
  120.  
  121.         /* PWM capture configuration */
  122.         TIM_ICInitTypeDef icconf = {
  123.                 .TIM_Channel = DHT_TIMER_CHANNEL,
  124.                 .TIM_ICPolarity = TIM_ICPolarity_Falling,
  125.                 .TIM_ICSelection = TIM_ICSelection_DirectTI,
  126.                 .TIM_ICPrescaler = TIM_ICPSC_DIV1,
  127.                 .TIM_ICFilter = 0x3,
  128.         };
  129.         TIM_PWMIConfig(DHT_TIMER, &icconf);
  130.  
  131.         TIM_SelectInputTrigger(DHT_TIMER, TIM_TS_TI1FP1); /* Select the TIM3 Input Trigger: TI1FP1 */
  132.         TIM_SelectSlaveMode(DHT_TIMER, TIM_SlaveMode_Reset); /* Select the slave Mode: Reset Mode */
  133.         TIM_SelectMasterSlaveMode(DHT_TIMER, TIM_MasterSlaveMode_Enable); /* Enable the Master/Slave Mode */
  134.         /* Configures the TIM Update Request Interrupt source: counter overflow */
  135.         TIM_UpdateRequestConfig(DHT_TIMER, TIM_UpdateSource_Regular);
  136.  
  137.         DHT_TIMER->CNT = 0;
  138.         DHT_TIMER->ARR = DHT_BIT_TIMEOUT_US; /* Set the TIM auto-reload register */
  139.         DHT_TIMER->SR = ~(TIM_FLAG_Update | TIM_FLAG_CC1);
  140.  
  141.         return 0;
  142. }
  143.  
  144. void DHT_IRQ_HANDLER(void) {
  145.         portBASE_TYPE preempt = pdFALSE;
  146.  
  147.         if(DHT_TIMER->SR & TIM_FLAG_CC1) {
  148.                 pwm_data.low = DHT_TIMER->CCR2;
  149.                 pwm_data.period = DHT_TIMER->CCR1;
  150.  
  151.                 DHT_TIMER->SR = ~TIM_FLAG_CC1;
  152.  
  153.                 xSemaphoreGiveFromISR(irq_sem, &preempt);
  154.  
  155.         } else if(DHT_TIMER->SR & TIM_FLAG_Update) {
  156.                 /* timeout */
  157.  
  158.                 DHT_TIMER->SR = ~TIM_FLAG_Update;
  159.                 stop_timer();
  160.  
  161.                 pwm_data.period = 0;
  162.                 xSemaphoreGiveFromISR(irq_sem, &preempt);
  163.         }
  164.  
  165.         portEND_SWITCHING_ISR(preempt);
  166. }
  167.  
  168. static inline void start_timer() {
  169.         DHT_TIMER->DIER |= (TIM_IT_Update | TIM_IT_CC1);
  170.         DHT_TIMER->CNT = 0;
  171.         DHT_TIMER->SR = ~(TIM_FLAG_Update | TIM_FLAG_CC1);
  172.     DHT_TIMER->CR1 |= TIM_CR1_CEN;
  173. }
  174.  
  175. static inline void stop_timer() {
  176.         DHT_TIMER->DIER &= ~(TIM_IT_Update | TIM_IT_CC1);
  177.     DHT_TIMER->CR1 &= ~TIM_CR1_CEN;
  178. }
  179.  
  180. static void dht_thread(void *data) {
  181.         while(1) {
  182.                 /* wait for read request */
  183.                 dht_read_t *req;
  184.                 if(!xQueueReceive(cmd_msgbox, &req, portMAX_DELAY)) continue;
  185.  
  186.                 /* send start pulse */
  187.                 gpio_output();
  188.                 DHT_GPIO->BRR = DHT_PIN; /* 0 */
  189.                 vTaskDelay(DHT_START_PULSE_MS / portTICK_RATE_MS);
  190.                 DHT_GPIO->BSRR = DHT_PIN; /* Hi-Z */
  191.                 gpio_input();
  192.  
  193.                 start_timer();
  194.  
  195.                 /* skip first falling edge */
  196.                 int i;
  197.                 for(i = 0; i < 2; i++) {
  198.                         /* IRQ timeout or receive timeout */
  199.                         if(!xSemaphoreTake(irq_sem, DHT_IRQ_TIMEOUT_MS / portTICK_RATE_MS)) {
  200.                                 req->error = DHT_IRQ_TIMEOUT;
  201.                                 goto reply;
  202.                         }
  203.                         if(!pwm_data.period) {
  204.                                 req->error = DHT_TIMEOUT;
  205.                                 goto reply;
  206.                         }
  207.                 }
  208.                 /* start sequence received */
  209.                 if(!PERIOD_OK(&pwm_data, 80, 80)) {
  210.                         req->error = DHT_DECODE_ERROR;
  211.                         goto reply;
  212.                 }
  213.  
  214.                 for(i = 0; i < DHT_PKT_SIZE; i++) {
  215.                         unsigned int mask = 0x80;
  216.                         uint8_t byte = 0;
  217.                         while(mask) {
  218.                                 if(!xSemaphoreTake(irq_sem, DHT_IRQ_TIMEOUT_MS / portTICK_RATE_MS)) {
  219.                                         req->error = DHT_IRQ_TIMEOUT;
  220.                                         goto reply;
  221.                                 }
  222.                                 if(!pwm_data.period) {
  223.                                         req->error = DHT_TIMEOUT;
  224.                                         goto reply;
  225.                                 }
  226.  
  227.                                 /* next bit received */
  228.                                 if(PERIOD_OK(&pwm_data, 50, 70)) {
  229.                                         byte |= mask; /* 1 */
  230.                                 } else if(!PERIOD_OK(&pwm_data, 50, 27)) {
  231.                                         req->error = DHT_DECODE_ERROR;
  232.                                         goto reply;
  233.                                 }
  234.  
  235.                                 mask >>= 1;
  236.                         }
  237.                         req->data[i] = byte;
  238.                 }
  239.                 req->error = DHT_NO_ERROR;
  240. reply:
  241.                 stop_timer();
  242.                 xSemaphoreGive(req->sem);
  243.         }
  244. }
  245.  
  246. int dht_read(xSemaphoreHandle read_sem, int *temperature, int *humidity, dht_error_t *error) {
  247.         dht_read_t rd;
  248.         dht_read_t *rd_p = &rd;
  249.  
  250.         xSemaphoreTake(read_sem, 0); /* to be sure */
  251.         rd_p->sem = read_sem;
  252.         xQueueSend(cmd_msgbox, &rd_p, portMAX_DELAY);
  253.  
  254.         /* wait for reply */
  255.         if(!xSemaphoreTake(read_sem, DHT_PKT_TIMEOUT_MS / portTICK_RATE_MS)) {
  256.                 *error = DHT_RCV_TIMEOUT;
  257.                 return -1;
  258.         }
  259.  
  260.         if(rd.error != DHT_NO_ERROR) {
  261.                 *error = rd.error;
  262.                 return -1;
  263.         }
  264.  
  265.         /* compute checksum */
  266.         unsigned int sum = 0;
  267.         int i;
  268.         for(i = 0; i < DHT_PKT_SIZE - 1; i++) sum += rd.data[i];
  269.         if((sum & 0xff) != rd.data[i]) {
  270.                 *error = DHT_CHECKSUM_ERROR;
  271.                 return -1;
  272.         }
  273.  
  274.         /* read 16 bit humidity value */
  275.         *humidity = ((unsigned int)rd.data[0] << 8) |
  276.                                 (unsigned int)rd.data[1];
  277.  
  278.         /* read 16 bit temperature value */
  279.         int val = ((unsigned int)rd.data[2] << 8) |
  280.                                 (unsigned int)rd.data[3];
  281.         *temperature = val & 0x8000 ? -(val & ~0x8000) : val;
  282.  
  283.         return 0;
  284. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top