Решил поделиться опытом работы с цифровым датчиком температуры и влажности DHT22. Для передачи данных датчик использует всего одну ногу с выходом открытый коллектор, ее нужно подтянуть к плюсу через резистор 4.7к. Для начала передачи данных от датчика, МК должен "прижать" линию к минусу как минимум на 1 мс. После этого датчик пошлет импульс присутствия, после чего выдаст в шину 40 бит данных (2 байта - значение влажности, 2 байта - значение температуры, 1 байт - контрольная сумма (сумма 4 байт данных)). Данные передаються старшим вперед.
Передача бита начинаеться с прижимания линии к минусу на время 50мкс, после чего датчик отпускает линию на время зависимое от того что он передает, если "0" - 26 мкс, если "1" - 70 мкс.
Судя по ДШ максимальная периодичность опроса 2 раза в секунду.
Используя таймер с двумя каналами захвата/сравнения можно реализовать опрос датчика практически 100% на аппаратном уровне. В МК для формирования сигнала "старт" и приема данных используеться выход канала таймера, который настроен как выход с открытым стоком. Таймер настроен на периодичность срабатывания 10 мкс, и переполнением 0.5 с. Для формирования сигнала "старт" используеться канал сравнения в режиме ШИМ + канал ПДП. По прерыванию канала сравнения (начало формирования сигнала "старт"), включаеться в работу 2 канал таймера в режиме захвата + ПДП для складирования данных в буфер. По прерыванию канала ПДП, после приема 43 захваченных бит производиться разбор буфера, расчет контрольной суммы и сохранение данных температуры и влажности.
Так выглядит структурная схема таймера:
Инициализация таймера и ПДП:
void TIM2_Init(void) { // настраиваем на работу с DHT22 GPIO_SetMode((gpOutput_AlternateOpenDrain | gp50MHz), PA0); DMA1_Ch2_Init(); DMA1_Ch7_Init(); TIM2->PSC = 719; // настраиваем на период 10 мкс и переполнение на 100мс TIM2->ARR = 49999; TIM2->SR = 0; TIM2->DIER = TIM_DIER_UDE | TIM_DIER_CC1IE | TIM_DIER_CC2DE; TIM2->CCMR1 = TIM_CCMR1_OC1M | TIM_CCMR1_CC2S_1; TIM2->CR1 = TIM_CR1_ARPE; TIM2->EGR = TIM_EGR_UG; TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC2P; TIM2->CR1 |= TIM_CR1_CEN; } void DMA1_Ch2_Init(void) { DMA1_Channel2->CCR = 0; DMA1_Channel2->CMAR = (uint32_t) &DHT22_TimDelayBuff[0]; DMA1_Channel2->CPAR = (uint32_t) &TIM2->CCR1; DMA1_Channel2->CNDTR = sizeof(DHT22_TimDelayBuff)/sizeof(*DHT22_TimDelayBuff); DMA1_Channel2->CCR = DMA_CCR2_PL | DMA_CCR2_MSIZE_0 | DMA_CCR2_PSIZE_1 | DMA_CCR2_MINC | DMA_CCR2_CIRC | DMA_CCR2_DIR; DMA1->IFCR = DMA_IFCR_CGIF2 | DMA_IFCR_CHTIF2 | DMA_IFCR_CTCIF2 | DMA_IFCR_CTEIF2; DMA1_Channel2->CCR |= DMA_CCR5_EN; } void DMA1_Ch7_Init(void) { DMA1_Channel7->CCR = 0; DMA1_Channel7->CMAR = (uint32_t) &DHT22_DataBuff[0]; DMA1_Channel7->CPAR = (uint32_t) &TIM2->CCR2; DMA1_Channel7->CNDTR = sizeof(DHT22_DataBuff)/sizeof(*DHT22_DataBuff); DMA1_Channel7->CCR = DMA_CCR7_PL | DMA_CCR7_MSIZE_0 | DMA_CCR7_PSIZE_1 | DMA_CCR7_MINC | DMA_CCR7_TCIE; DMA1->IFCR = DMA_IFCR_CGIF7 | DMA_IFCR_CHTIF7 | DMA_IFCR_CTCIF7 | DMA_IFCR_CTEIF7; }
Обработчик прерывания таймера:
void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_CC1IF) { DMA1_Channel7->CCR &= ~DMA_CCR7_EN; DMA1_Channel7->CMAR = (uint32_t) &DHT22_DataBuff[0]; DMA1_Channel7->CNDTR = sizeof(DHT22_DataBuff)/sizeof(*DHT22_DataBuff); DMA1_Channel7->CCR |= DMA_CCR7_EN; TIM2->CCER |= TIM_CCER_CC2E; TIM2->SR &= ~TIM_SR_CC1IF; } }
Обработчик прерывания канала ПДП:
void DMA1_Channel7_IRQHandler(void) { uint64_t tData = 0; uint8_t *tDataBuff = (uint8_t*)&tData; uint16_t a; uint32_t *b; uint8_t tCRC; if(DMA1->ISR & DMA_ISR_TCIF7) {// приняли данные от DHT22 TIM2->CCER &= ~TIM_CCER_CC2E; b = BITBAND_RAMADR((uint32_t)&tData, 0); for(a = 0; a < 40; a++) if((DHT22_DataBuff[a + 2] - DHT22_DataBuff[a + 1]) > 10) b[39 - a] = 1; else b[39 - a] = 0; // подсчет CRC tCRC = 0; for(a = 4; a > 0; a--) tCRC = tCRC + tDataBuff[a]; // if(tCRC == tDataBuff[0]) { a = (uint16_t)(tData >> 24); H = (float)a / 10; a = (uint16_t)(tData >> 8); T = (float)a / 10; } else { } DMA1->IFCR = DMA_IFCR_CTCIF7; } }