QUOTE (Jenya7 @ Jun 30 2016, 11:05)

Единственное решение к которому я пришел - прерывания таймера и State Machine в прерывании.
Я делал на одном прерывани таймера, использовал три модуля захвата/сравнения (Capture/Compare) и событие переполнения (UIF) таймера. Конечного автомата нет, точнее он выродился в проверку флагов CCxIF в обработчике прерывания. Получилось довольно компактно.
При обмене битом первый модуль сравнения настраиваю на tLOW1, второй на tRDW, третий на tLOW0, ARR на tSLOT.
Для сброса первый модуль настраиваю на tRSTL, второй на tRSTL+tPDLOW, третий на tRSTL+tRSTH, ARR на tRSTL+tRSTH.
Перед запуском прерываний задаю передаваемые биты (прием = передача всех единиц) и количество битов. В прерывании уменьшаю счетчик и по достижении сигналю флаг окончания обмена.
С эти же обработчиком удалось сделать и поиск устройств (там обмен идет двухбитовыми посылками).
Прерывание пришлось сделать высокоприоритетным, поскольку время tRDW-tLOW1 достаточно мало.
CODE
// Pin_out, Pin_in - ссылки на бит линии в bitband-области. Если процессор не поддерживает bitband, то можно вставить команды управления портом.
uint8_t one_wire::exchange(uint8_t in, uint_fast8_t bits)
{ // биты передаются начиная с младшего, принимаются в старший.
Shifter = in;
Bit_counter = bits;
{
TCritSect cs;
Pin_out = 0;
Timer->CR1 = 0
| 0 * (TIM_CR1_CKD & -TIM_CR1_CKD) // Dead-time clock, 0 = timer clock
| 0 * TIM_CR1_ARPE // ARR buffering
| 0 * (TIM_CR1_CMS & -TIM_CR1_CMS) // 0 = edge-aligned, 1...3 = center-aligned mode 1...3
| 0 * TIM_CR1_DIR // Direction, 0 = up, 1 = down
| 0 * TIM_CR1_OPM // One pulse mode, 1 = stop counter at update event
| 0 * TIM_CR1_URS // Update request source, 0 = any, 1 = only counter under-/overflow
| 0 * TIM_CR1_UDIS // Update event generation disabling
| 1 * TIM_CR1_CEN // Counter enable
;
}
Done_flag.wait();
return Shifter;
}
void one_wire::handler()
{
uint_fast16_t Status = Timer->SR;
Timer->SR = ~Status;
if(Status & TIM_SR_CC1IF) // bit output time
{
if(Shifter & (1 << 0))
Pin_out = 1;
Shifter >>= 1;
}
if(Status & TIM_SR_CC2IF) // bus sample time
{
if(Pin_in)
Shifter |= (1 << 7);
}
if(Status & TIM_SR_CC3IF) // bus release time
Pin_out = 1;
if(Status & TIM_SR_UIF) // end of slot
{
if(!--Bit_counter)
{
Timer->CR1 = 0; // stop timer
Timer->CNT = 0;
OS::TISRW ISR_wrapper;
Done_flag.signal_isr();
}
else
Pin_out = 0; // start new slot
}
}
bool one_wire::reset()
{
Mutex.lock();
Timer->ARR = T3_CLK / 2 * uint64_t(480 + 480) / (1000 * 1000); // slot time: 480 uS + 480 uS
Timer->CCR1 = T3_CLK / 2 * uint64_t(480) / (1000 * 1000); // bus release: 480 uS
Timer->CCR2 = T3_CLK / 2 * uint64_t(480 + 60) / (1000 * 1000); // bus sample: 480 + 60 uS
Timer->CCR3 = T3_CLK / 2 * uint64_t(480 + 480) / (1000 * 1000); // dummy
if(exchange((1 << 0), 1) & (1 <<7))
{
Mutex.unlock();
return false;
}
Timer->ARR = T3_CLK / 2 * uint64_t(15 + 45 + 45) / (1000 * 1000); // slot time: 15 + 45 + 45 uS
Timer->CCR1 = T3_CLK / 2 * uint64_t(9) / (1000 * 1000); // bit write: 9 uS
Timer->CCR2 = T3_CLK / 2 * uint64_t(18) / (1000 * 1000); // bus sample: 18 uS
Timer->CCR3 = T3_CLK / 2 * uint64_t(60) / (1000 * 1000); // bus release: 60 uS
return true;
}
uint8_t one_wire::receive()
{
return exchange(0xFF, 8);
}
bool one_wire::transmit(uint8_t data)
{
return exchange(data, 8) == data;
}
Сейчас посмотрел на этот код - можно было использовать one-pulse mode. Надо будет переделать.