Закодировав данные манчестерским кодом следуем по наиболее логичному пути, который ведет нас к процессу декодирования. Поставим задачу взять непосредственно те самые данные, которые были сгенерированы в предыдущей статье, подать их на декодер и сверить полученный результат с исходной последовательностью.
За основу возьмем проект из уже упомянутой статьи и добавим в него функции для декодирования манчестерского кода. Сигнал генерируется на PA3, возьмем другой пин, и на него подадим этот сигнал, физически замкнув ножки. Пусть будет PA4, почему бы нет:
Выбранный вывод настраиваем на генерацию прерываний при изменении уровня сигнала, по обоим фронтам сигнала, потому что нам нужны будут и передний и задний фронты для работы:
Помимо этого в CubeMx ничего добавлять или менять не потребуется, таймер будем использовать тот же, насколько я помню - TIM2.
Декодирование манчестерского кода.
Для начала пройдемся по физической сути процесса декодирования. Я его разбил на несколько этапов, что нам добавит наглядности и прозрачности в рассмотрении протекающих явлений. Итак, список такой:
- синхронизация по фронтам
- синхронизация по данным
- декодирование данных
Синхронизация по фронтам.
Берем рассмотренный нами манчестерский код:
Нижний сигнал мы будем принимать на входе контроллера. И, в целом, все просто - если передний фронт (переход от низкого уровня к высокому, 0 => 1), то это должно декодироваться в бит "1". Если задний фронт (переход от высокого уровня к низкому, 1 => 0), то это закодированный бит "0".
Но из графика очевидно, что нам нужно брать не все фронты, а вполне определенные, они помечены зеленой меткой. Напротив, изменения сигнала с красными метками мы должны пропускать.
Не проблема - зная период кодирования можно отфильтровать "ненужные" перепады, поскольку время между значимыми пепепадами гарантированно соответствует этому периоду. Поскольку в реальном мире все неидеально, длительности могут плавать, поэтому зададим величину в 75% от периода кодирования. Если время между фронтами больше, то работаем, если время меньше, пропускаем фронт.
Осталось определить отправную точку. То есть для того, чтобы использовать эту логику с длительностями и периодами, необходимо знать хотя бы один значимый фронт (с зеленой меткой), чтобы относительно него уже вести отсчет. Для этой цели послужит следующий механизм.
Опять же из графика можно заметить, что два перепада разного(!) направления, разделенные периодом кодирования, гарантированно являются значимыми. И не важно, передний ли это фронт после заднего или задний после переднего. В этих случаях получаем:
- передний и задний фронт, время между которыми соответствует периоду - это закодированные биты "1" и "0"
- задний и передний фронт с аналогичным временным интервалом - это биты "0" и "1" исходной последовательности.
В итоге процесс, названный мной синхронизацией по фронтам, заключается в комбинации этих двух механизмов. Сначала ищем соседние фронты, разделенные периодом, и с разным направлением перепада. Затем относительно этих "верных" фронтов берем следующие, также разделенные временем, равным (в реальном мире примерно равным) периоду кодирования.
Синхронизация по данным.
Переходим ко второму этапу - синхронизации по данным. Снова отсылка к статье про манчестерское кодирование, где мы специально добавили к передаваемым данным байты синхрополя. Они необходимы для того, что определить, где начинаются информационные байты.
Здесь реализация будет заключаться в следующем. Возьмем 16-битную переменную, в которую будем последовательно, бит за битом, сохранять декодированные значения. Выглядит это все так:
После каждого нового бита сохраняем его в переменную, сдвигая принятые ранее биты. В результате, в определенный момент времени значение в этой переменной будет равно значению синхрополя. Именно этот момент нам и требуется отловить, чтобы в дальнейшем относительно него считывать последующие биты.
Синхронизировавшись таким образом по данным, начинаем третий этап, собственно, сохранение и анализ информационных, то есть полезных, данных.
Декодирование данных.
Здесь механизм все тот же - смотрим на фронты, разделенные периодом кодирования. Разница только в том, что сохранять их будем не в переменную со сдвигом, а в массив. Физически суть все та же, так что переходим к практической реализации.
Декодирование манчестерского кода на STM32.
Вход для данных мы активировали, поэтому сразу к делу. Работаем в тех же файлах:
- manchester_code.c
- manchester_code.h
По той же схеме, начинаем с добавления констант:
#define MANCH_INPUT_PORT GPIOA #define MANCH_INPUT_PIN GPIO_PIN_4 #define MANCH_DECODE_TIMER_PERIOD_US 10 #define MANCH_DECODE_TIMER_MAX MANCH_BIT_TIME_US / MANCH_DECODE_TIMER_PERIOD_US #define MANCH_DECODE_TIMER_THRESHOLD MANCH_DECODE_TIMER_MAX * 3 / 4
Я привожу здесь только то, что добавляется относительно текущего проекта с манчестерским кодированием. Полный код будет под спойлерами в конце статьи. Там же и ссылка на полный проект для STM32F103C8.
Итак, таймер у нас тот же, значит и период его такой же - 10 мкс. Рассчитываем по аналогии MANCH_DECODE_TIMER_MAX
- это период кодирования, он же - длительность передачи одного бита. Кроме того, задаем порог, который мы обсудили, равный 75% от периода кодирования:
#define MANCH_DECODE_TIMER_THRESHOLD MANCH_DECODE_TIMER_MAX * 3 / 4
Объявляем возможные состояния декодера и, для удобства, фронтов сигнала:
typedef enum { NOT_SYNC, BIT_SYNC, DATA_SYNC, DATA_READY } MANCH_DecodeState; typedef enum { NONE, RAISING_EDGE, FALLING_EDGE } MANCH_DecodeEdge;
Структура для хранения данных остается неизменной:
typedef struct MANCH_Data { uint8_t data[MANCH_BYTES_NUM]; uint16_t bitStream; uint16_t byteIdx; uint16_t bytesNum; uint8_t bitIdx; uint8_t active; } MANCH_Data;
По поводу состояний декодера:
- исходное значение -
NOT_SYNC
- соответствует отсутствию всяческой синхронизации. На данном этапе нам нужно осуществить синхронизацию по фронтам. - после синхронизации по фронтам декодер переходит в состояние
BIT_SYNC
и приступает к синхронизации по данным. - далее - состояние
DATA_SYNC
- синхронизация завершена, идет декодирование полезной информации. - по окончанию -
DATA_READY
. В данном случае можно снова вернуться в исходную точку (NOT_SYNC
) для приема и декодирования последующих данных, либо заняться анализом принятых, зависит от конкретной задачи.
Теперь в файле manchester_code.c объявляем переменные (я опять же привожу только отличия относительно исходного проекта):
static MANCH_Data decodeData; static MANCH_DecodeEdge curEdge = NONE; static MANCH_DecodeEdge prevEdge = NONE; static MANCH_DecodeState decodeState = NOT_SYNC; static uint16_t decodeTimerCnt = 0;
Все, в принципе, понятно из названий:
decodeData
- экземпляр основной структуры для работы с кодом Манчестер-IIcurEdge
,prevEdge
- текущий и предыдущий фронты принятого сигналаdecodeState
- состояние декодераdecodeTimerCnt
- отсчетный счетчик таймера для всех движений, связанных с анализом временных интервалов.
Следующий шаг, вспомогательные функции. Определение уровня на входе PA4:
/*----------------------------------------------------------------------------*/ static uint8_t GetInput() { uint8_t state = (uint8_t)HAL_GPIO_ReadPin(MANCH_INPUT_PORT, MANCH_INPUT_PIN); return state; } /*----------------------------------------------------------------------------*/
Далее по аналогии с GetDataBit()
, которую мы использовали при кодировании, создаем функцию SetDataBit()
, выполняющую обратную операцию, то есть установку нужного бита в массиве данных data[]
в структуре:
/*----------------------------------------------------------------------------*/ static void SetDataBit(MANCH_Data* manchData, uint8_t bit) { uint8_t curByteIdx = manchData->byteIdx; uint8_t curBitIdx = manchData->bitIdx; if (bit == 1) { manchData->data[curByteIdx] |= (1 << curBitIdx); } } /*----------------------------------------------------------------------------*/
В callback'е по переполнению таймера пока добавляем только инкрементирование счетчика:
// Decoding process if (decodeData.active == 1) { decodeTimerCnt++; }
Весь основной функционал будет в прерывании по изменению уровня сигнала на PA4. Рассмотрим полный код, затем пройдемся по нему подробно:
/*----------------------------------------------------------------------------*/ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == MANCH_INPUT_PIN) { uint8_t inputState = GetInput(); if (inputState == 0) { curEdge = FALLING_EDGE; } else { curEdge = RAISING_EDGE; } switch (decodeState) { case NOT_SYNC: if (decodeData.active == 0) { decodeData.active = 1; decodeTimerCnt = 0; } else { if (((curEdge == FALLING_EDGE) && (prevEdge == RAISING_EDGE)) || ((curEdge == RAISING_EDGE) && (prevEdge == FALLING_EDGE))) { if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == FALLING_EDGE) { decodeData.bitStream = 0x4000; decodeData.bitStream >>= 1; } else { decodeData.bitStream = 0x8000; decodeData.bitStream >>= 1; } for (uint8_t i = 0; i < MANCH_BYTES_NUM; i++) { decodeData.data[i] = 0x00; } decodeState = BIT_SYNC; } decodeTimerCnt = 0; } } break; case BIT_SYNC: if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == RAISING_EDGE) { decodeData.bitStream |= 0x8000; } if (decodeData.bitStream == MANCH_SYNC_FIELD) { decodeState = DATA_SYNC; decodeData.data[0] = decodeData.bitStream & 0xFF; decodeData.data[1] = (decodeData.bitStream & 0xFF00) >> 8; decodeData.bitIdx = 0; decodeData.byteIdx = MANCH_SYNC_BYTES_NUM; decodeData.bytesNum = MANCH_DATA_BYTES_NUM + MANCH_SYNC_BYTES_NUM; } else { decodeData.bitStream >>= 1; } decodeTimerCnt = 0; } break; case DATA_SYNC: if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == RAISING_EDGE) { SetDataBit(&decodeData, 1); } decodeData.bitIdx++; if (decodeData.bitIdx == (MANCH_BITS_IN_BYTE_NUM)) { decodeData.bitIdx = 0; decodeData.byteIdx++; if (decodeData.byteIdx == decodeData.bytesNum) { decodeData.active = 0; curEdge = NONE; prevEdge = NONE; decodeState = DATA_READY; // Data is ready MANCH_DataReadyCallback(); } } decodeTimerCnt = 0; } break; case DATA_READY: break; default: break; } prevEdge = curEdge; } } /*----------------------------------------------------------------------------*/
Для начала определяем уровень сигнала на входе, что позволяет нам судить о фронте импульса:
uint8_t inputState = GetInput(); if (inputState == 0) { curEdge = FALLING_EDGE; } else { curEdge = RAISING_EDGE; }
На выходе из функции обновляем значение для предыдущего фронта:
prevEdge = curEdge;
Остальной функционал в switch (decodeState)
и подчиняется тем алгоритмам и явлениям, которые мы обсудили в начале статьи:
case NOT_SYNC: if (decodeData.active == 0) { decodeData.active = 1; decodeTimerCnt = 0; } else { if (((curEdge == FALLING_EDGE) && (prevEdge == RAISING_EDGE)) || ((curEdge == RAISING_EDGE) && (prevEdge == FALLING_EDGE))) { if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == FALLING_EDGE) { decodeData.bitStream = 0x4000; decodeData.bitStream >>= 1; } else { decodeData.bitStream = 0x8000; decodeData.bitStream >>= 1; } for (uint8_t i = 0; i < MANCH_BYTES_NUM; i++) { decodeData.data[i] = 0x00; } decodeState = BIT_SYNC; } decodeTimerCnt = 0; } } break;
В исходном состоянии запускаем процесс декодирования через флаг decodeData.active
, что по сути приводит к тому, что в прерывании по таймеру начинается процесс инкрементирования счетчика decodeTimerCnt
.
Ищем соседние фронты с разными перепадами, разделенные периодом кодирования. Для этого сравниваем счетчик с величиной порога MANCH_DECODE_TIMER_THRESHOLD
. Если находим последовательные перепады передний фронт-задний фронт, то это означает, что декодированы биты "1" и "0", которые помещаем в decodeData.bitStream
:
if (curEdge == FALLING_EDGE) { decodeData.bitStream = 0x4000; decodeData.bitStream >>= 1; }
После выполнения этих операций:
Это соответствует тому, что сначала мы декодировали "1", затем "0". Аналогично при противоположных фронтах, только с другим значением:
decodeData.bitStream = 0x8000; decodeData.bitStream >>= 1;
На этом декодер готов перейти в следующее состояние:
decodeState = BIT_SYNC;
Начинаем синхронизацию по данным. Для этого продолжаем заполнять decodeData.bitStream
битами. Если обнаружили "1", то выставляем в "1" старший бит:
if (curEdge == RAISING_EDGE) { decodeData.bitStream |= 0x8000; }
В случае "0" ничего не делаем, за счет сдвига получим требуемое значение:
decodeData.bitStream >>= 1;
Вот и все, спокойно и непринужденно анализируем декодированные биты в ожидании совпадения со значением синхрополя:
if (decodeData.bitStream == MANCH_SYNC_FIELD) { decodeState = DATA_SYNC; decodeData.data[0] = decodeData.bitStream & 0xFF; decodeData.data[1] = (decodeData.bitStream & 0xFF00) >> 8; decodeData.bitIdx = 0; decodeData.byteIdx = MANCH_SYNC_BYTES_NUM; decodeData.bytesNum = MANCH_DATA_BYTES_NUM + MANCH_SYNC_BYTES_NUM; }
В случае совпадения закидываем байты в decodeData.data[]
, обнуляем счетчики битов и байт, и переводим декодер в состояние DATA_SYNC
. После чего продолжаем делать то же самое, за исключением того, что биты записываем не в decodeData.bitStream
, а в массив decodeData.data[]
при помощи SetDataBit()
. Естественно, этому сопутствует увеличение индексов текущих битов и байт.
Логика такая же, при декодировании бита "1" - выставляем его в массиве - SetDataBit(&decodeData, 1)
. Если "0", то ничего не требуется, нужный "0" окажется на своем месте за счет увеличения индекса текущего бита.
Когда количество декодированных байт равно максимальному количеству (MANCH_DATA_BYTES_NUM
+ MANCH_SYNC_BYTES_NUM
), заданному в хэдере, останавливаем процесс:
if (decodeData.byteIdx == decodeData.bytesNum) { decodeData.active = 0; curEdge = NONE; prevEdge = NONE; decodeState = DATA_READY; // Data is ready MANCH_DataReadyCallback(); }
В этом тестовом проекте у меня задано 10 байт - 8 информационных + 2 байта синхрополя. По окончании декодирования вызываем callback MANCH_DataReadyCallback()
, объявленный в этом же файле чуть выше:
/*----------------------------------------------------------------------------*/ __weak void MANCH_DataReadyCallback() { } /*----------------------------------------------------------------------------*/
Работает как и с колбэками из HAL, то есть эту функцию можно переопределить в другом файле и вставить свой код. Например, я добавил в main.c:
/* USER CODE BEGIN 4 */ void MANCH_DataReadyCallback() { MANCH_DecodeReset(); } /* USER CODE END 4 */
Вызывается еще одна вспомогательная функция из manchester_code.c, которая лишь переводит декодер в исходное состояние:
/*----------------------------------------------------------------------------*/ void MANCH_DecodeReset() { decodeState = NOT_SYNC; } /*----------------------------------------------------------------------------*/
Осталось учесть один немаловажный нюанс. В данной форме процесс декодирования завершится только тогда, когда принято MANCH_BYTES_NUM
байт. Если исходная посылка будет короче, то произойдет коллапс, данные перепутаются. Поэтому добавим еще одно условие, по которому декодирование признается завершенным. Если в течение некоторого интервала времени, гарантированно превышающего длительность кодирования, не было обнаружено изменений уровня на входе, то останавливаем работу. Я возьму время, равное 3-м периодам кодирования. Обновляем код прерывания по таймеру:
// Decoding process if (decodeData.active == 1) { if (decodeState == DATA_SYNC) { if (decodeTimerCnt >= (3 * MANCH_DECODE_TIMER_MAX)) { decodeTimerCnt = 0; decodeData.active = 0; curEdge = NONE; prevEdge = NONE; decodeState = DATA_READY; // Data is ready MANCH_DataReadyCallback(); } } decodeTimerCnt++; }
Теперь все в норме, тестируем. В main()
у нас по-прежнему осуществляется манчестерское кодирование:
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ MANCH_Encode(txData, 8); HAL_Delay(500); }
Для проверки декодирования встаем брейкпинтом на callback в main.c:
/* USER CODE BEGIN 4 */ void MANCH_DataReadyCallback() { MANCH_DecodeReset(); } /* USER CODE END 4 */
В результате в полученных данных получаем полное соответствие исходным данным 👍
И на этом заканчиваем работу с кодом Манчестер-II, надеюсь, все было понятно, тем не менее вопросы на форуме или в комментариях категорически приветствуются )
P. S. Да, кстати, здесь при декодировании мы использовали информацию о периоде кодирования, но эта величина не всегда известна. Если будет спрос и интерес, опишу как провести аналогичную деятельность по декодированию при отсутствии априорной информации о длительности передачи бита.
/** ****************************************************************************** * @file : manchester_code.c * @brief : Manchester code driver * @author : MicroTechnics (microtechnics.ru) ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "manchester_code.h" /* Declarations and definitions ----------------------------------------------*/ static uint8_t virtTact = 1; static MANCH_Data encodeData; static MANCH_Data decodeData; static MANCH_DecodeEdge curEdge = NONE; static MANCH_DecodeEdge prevEdge = NONE; static MANCH_DecodeState decodeState = NOT_SYNC; static uint16_t encodeTimerCnt = 0; static uint16_t decodeTimerCnt = 0; extern TIM_HandleTypeDef htim2; /* Functions -----------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ static void SetOutput(uint8_t state) { HAL_GPIO_WritePin(MANCH_OUTPUT_PORT, MANCH_OUTPUT_PIN, (GPIO_PinState)state); } /*----------------------------------------------------------------------------*/ static uint8_t GetInput() { uint8_t state = (uint8_t)HAL_GPIO_ReadPin(MANCH_INPUT_PORT, MANCH_INPUT_PIN); return state; } /*----------------------------------------------------------------------------*/ void MANCH_Encode(uint8_t* data, uint8_t size) { encodeData.bitIdx = 0; encodeData.byteIdx = 0; if (size > MANCH_DATA_BYTES_NUM) { encodeData.bytesNum = MANCH_DATA_BYTES_NUM + MANCH_SYNC_BYTES_NUM; } else { encodeData.bytesNum = size + MANCH_SYNC_BYTES_NUM; } memcpy(&encodeData.data[MANCH_SYNC_BYTES_NUM], data, encodeData.bytesNum - MANCH_SYNC_BYTES_NUM); encodeData.data[0] = MANCH_SYNC_FIELD & 0xFF; encodeData.data[1] = (MANCH_SYNC_FIELD & 0xFF00) >> 8; encodeTimerCnt = 0; virtTact = 1; encodeData.active = 1; } /*----------------------------------------------------------------------------*/ static uint8_t GetDataBit(MANCH_Data* manchData) { uint8_t res; uint8_t curByte = manchData->data[manchData->byteIdx]; uint8_t curBitIdx = manchData->bitIdx; res = (curByte >> curBitIdx) & 0x01; return res; } /*----------------------------------------------------------------------------*/ static void SetDataBit(MANCH_Data* manchData, uint8_t bit) { uint8_t curByteIdx = manchData->byteIdx; uint8_t curBitIdx = manchData->bitIdx; if (bit == 1) { manchData->data[curByteIdx] |= (1 << curBitIdx); } } /*----------------------------------------------------------------------------*/ __weak void MANCH_DataReadyCallback() { } /*----------------------------------------------------------------------------*/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim2) { // Encoding process if (encodeData.active == 1) { if ((encodeTimerCnt == (MANCH_ENCODE_TIMER_MAX / 2)) || (encodeTimerCnt == MANCH_ENCODE_TIMER_MAX)) { uint8_t curCodeBit = GetDataBit(&encodeData); uint8_t curOutputBit = curCodeBit ^ virtTact; SetOutput(curOutputBit); virtTact ^= 0x01; } if (encodeTimerCnt == MANCH_ENCODE_TIMER_MAX) { encodeData.bitIdx++; if (encodeData.bitIdx == (MANCH_BITS_IN_BYTE_NUM)) { encodeData.bitIdx = 0; encodeData.byteIdx++; if (encodeData.byteIdx == encodeData.bytesNum) { encodeData.active = 0; } } encodeTimerCnt = 0; } encodeTimerCnt++; } // Decoding process if (decodeData.active == 1) { if (decodeState == DATA_SYNC) { if (decodeTimerCnt >= (3 * MANCH_DECODE_TIMER_MAX)) { decodeTimerCnt = 0; decodeData.active = 0; curEdge = NONE; prevEdge = NONE; decodeState = DATA_READY; // Data is ready MANCH_DataReadyCallback(); } } decodeTimerCnt++; } } } /*----------------------------------------------------------------------------*/ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == MANCH_INPUT_PIN) { uint8_t inputState = GetInput(); if (inputState == 0) { curEdge = FALLING_EDGE; } else { curEdge = RAISING_EDGE; } switch (decodeState) { case NOT_SYNC: if (decodeData.active == 0) { decodeData.active = 1; decodeTimerCnt = 0; } else { if (((curEdge == FALLING_EDGE) && (prevEdge == RAISING_EDGE)) || ((curEdge == RAISING_EDGE) && (prevEdge == FALLING_EDGE))) { if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == FALLING_EDGE) { decodeData.bitStream = 0x4000; decodeData.bitStream >>= 1; } else { decodeData.bitStream = 0x8000; decodeData.bitStream >>= 1; } for (uint8_t i = 0; i < MANCH_BYTES_NUM; i++) { decodeData.data[i] = 0x00; } decodeState = BIT_SYNC; } decodeTimerCnt = 0; } } break; case BIT_SYNC: if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == RAISING_EDGE) { decodeData.bitStream |= 0x8000; } if (decodeData.bitStream == MANCH_SYNC_FIELD) { decodeState = DATA_SYNC; decodeData.data[0] = decodeData.bitStream & 0xFF; decodeData.data[1] = (decodeData.bitStream & 0xFF00) >> 8; decodeData.bitIdx = 0; decodeData.byteIdx = MANCH_SYNC_BYTES_NUM; decodeData.bytesNum = MANCH_DATA_BYTES_NUM + MANCH_SYNC_BYTES_NUM; } else { decodeData.bitStream >>= 1; } decodeTimerCnt = 0; } break; case DATA_SYNC: if (decodeTimerCnt >= MANCH_DECODE_TIMER_THRESHOLD) { if (curEdge == RAISING_EDGE) { SetDataBit(&decodeData, 1); } decodeData.bitIdx++; if (decodeData.bitIdx == (MANCH_BITS_IN_BYTE_NUM)) { decodeData.bitIdx = 0; decodeData.byteIdx++; if (decodeData.byteIdx == decodeData.bytesNum) { decodeData.active = 0; curEdge = NONE; prevEdge = NONE; decodeState = DATA_READY; // Data is ready MANCH_DataReadyCallback(); } } decodeTimerCnt = 0; } break; case DATA_READY: break; default: break; } prevEdge = curEdge; } } /*----------------------------------------------------------------------------*/ void MANCH_DecodeReset() { decodeState = NOT_SYNC; } /*----------------------------------------------------------------------------*/
/** ****************************************************************************** * @file : manchester_code.h * @brief : Manchester code driver interface * @author : MicroTechnics (microtechnics.ru) ****************************************************************************** */ #ifndef MANCH_H #define MANCH_H /* Includes ------------------------------------------------------------------*/ #include "stm32f1xx_hal.h" #include <string.h> /* Declarations and definitions ----------------------------------------------*/ #define MANCH_OUTPUT_PORT GPIOA #define MANCH_OUTPUT_PIN GPIO_PIN_3 #define MANCH_INPUT_PORT GPIOA #define MANCH_INPUT_PIN GPIO_PIN_4 #define MANCH_ENCODE_TIMER_PERIOD_US 10 #define MANCH_BIT_TIME_US 100 #define MANCH_BYTES_NUM 10 #define MANCH_SYNC_BYTES_NUM 2 #define MANCH_DATA_BYTES_NUM MANCH_BYTES_NUM - MANCH_SYNC_BYTES_NUM #define MANCH_BITS_IN_BYTE_NUM 8 #define MANCH_SYNC_FIELD 0xAA55 #define MANCH_DECODE_TIMER_PERIOD_US 10 #define MANCH_ENCODE_TIMER_MAX MANCH_BIT_TIME_US / MANCH_ENCODE_TIMER_PERIOD_US #define MANCH_DECODE_TIMER_MAX MANCH_BIT_TIME_US / MANCH_DECODE_TIMER_PERIOD_US #define MANCH_DECODE_TIMER_THRESHOLD MANCH_DECODE_TIMER_MAX * 3 / 4 typedef enum { NOT_SYNC, BIT_SYNC, DATA_SYNC, DATA_READY } MANCH_DecodeState; typedef enum { NONE, RAISING_EDGE, FALLING_EDGE } MANCH_DecodeEdge; typedef struct MANCH_Data { uint8_t data[MANCH_BYTES_NUM]; uint16_t bitStream; uint16_t byteIdx; uint16_t bytesNum; uint8_t bitIdx; uint8_t active; } MANCH_Data; /* Functions -----------------------------------------------------------------*/ extern void MANCH_Encode(uint8_t* data, uint8_t size); extern void MANCH_DecodeReset(); #endif // #ifndef MANCH_H
Ссылка на проект: MT_ManchesterCode_Decode
Подскажите, а принятые байты можно передать наружу по SPI, не будет ли он мешать приему информации?
Не должно быть проблем с этим.
Еще один вопрос, при частоте манчестера 1Мгц этот код будет рабочим, хватит ли быстродействия контроллера.
Надо пробовать - соответственно параметры таймера и т. д. корректировать под задачу.
Спасибо! С нетерпением жду продолжения "как провести аналогичную деятельность по декодированию при отсутствии априорной информации о длительности передачи бита."
Эх, очень трудно выходит на все время найти )
Очень надо, подскажите в каком направлении копать?
Можно в течение некоторого времени захватывать передние и задние фронты всех импульсов на входе и сохранять в буфер, затем отсортировать их по значению, выкинуть пару крайних значений. В итоге должно получиться два набора значений, причем одно значение меньше второго в два раза (плюс-минус погрешность). Усреднить их и получим период искомый.
очень полезная статья. Спасибо большое за ваш труд.
Благодарю за отличный отзыв!
Доброго дня ! я правильно понимаю, что на основе этого кода, можно поднять протокол openterm для общения с котлами ?
Aveal подскажите , как можно изменить ваш код, чтобы получить общение с openterm ? пример описания https://ihormelnyk.com/Page/arduino_opentherm_controller достаточно одной команды. СПАСИБО !
Добрый день!
Навскидку, для формирования данных - убрать синхрополе:
На этом этапе можно пробовать отправлять данные, с приемом примерно аналогично, надо убрать обслуживание синхрополя.
I switched to Msb instead of Lsb in the system. But decodeData.data does not get the correct data, only 0. Can you help me where should I change in the decode process? (I observed that the signals switched to Msb with Logic analyzer.)
The best option is to analyze decodeData.bitStream values with the debugger. After that you can compare the expected bits and the actual bits in bitStream.