Advertisement
Guest User

am2302.c

a guest
Jan 22nd, 2013
1,777
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.66 KB | None | 0 0
  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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement