Решил поделиться опытом работы с цифровым датчиком температуры и влажности 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;
}
}