Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: UART+xmodem1k. передача файла
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Klaxons
Здравствуйте

Столкнулся со следующей проблемой, пытаюсь организовать передачу файла с компьютера на микроконтроллер STM32F4 при помощи uart и протокола xmodem1k.

Отправитель написан на java, сразу оговорюсь, написал на java также receiver, и передавал по xmodem1k файл на java, проблем никаких не было, стал писать приемщик на C под stm32 и заметил что, в какой-то момент данные в буфере смещаются.

Вот пример пакета, который шлет компьютер

Код
[b]2 1 -2[/b] -1 -40 -1 -32 0 16 74 70 73 70 0 1 1 1 0 1 0 1 0 0 -1 -37 0 67 0 . . . 233 [b]128 197[/b]


То есть первый байт я говорю, длину пакета (STX или SOH), второй байт - это номер пакета, 3 - инверсия номера пакета, далее данные по 128 или 1024 байта, последние два байта CRC16

И в какой-то момент я вижу что данные смещаются, например так

Код
[b]197 2 1[/b] -2 -1 -40 -1 -32 0 16 74 70 73 70 0 1 1 1 0 1 0 1 0 0 -1 -37 0 67 0 . . . [b]233 128[/b]


или даже так бывает

Код
[b]233 128 197[/b] 2 1 -2 -1 -40 -1 -32 0 16 74 70 73 70 0 1 1 1 0 1 0 1 0 0 -1 -37 0 67 0 . . .


То есть начало буфера получается в конце данных, при этом вроде как порядок байтов не меняется, то есть когда порядок начинается с 2 1 -2 ...
Я вижу, что CRC возвращает истину и пакет цел

Сначала думал, что проблема в отправители, но попробовал ставить паузу после отправки каждого байта все отправляет корректно, то есть я вижу последовательность байтов.

Еще такой момент, когда запускаю отладчик, и ставлю breakpoint на прием последнего байта, то получается чаще принимать данные в правильной последовательности.
То есть если запустить на плате прием данных, без прерывания отладчиком, то начинает зависать на втором пакете, всегда CRC не совпадает и программа отправляет NAK, а если поставить точку в отладчике, то с n раза пакет приходит в правильной последовательности и я сообщаю отправителю слать следующий пакет, иногда, конечно, приходит не в той последовательности, но в другой попытке - все приходит ровно. Но в отладчике также есть момент, когда в какой-то момент программа подвисает, то есть не дойдя до контрольной точки никак не реагирует, хотя отправитель послал пакет, либо после resume контрольной точки, не доходит до места отправки NAK или ACK, приходится хардверно отправлять NAK (на пользовательскую кнопку повешена такая задача)

Также пробовал перебирать последовательность принятых байт, смещая их на одну позицию назад, но это не очень удобно, если неизвестно на какой позиции находится настоящий первый элемент пакета.

Вот пример инициализации и приема данных в stm

Если, например, опустить дополнительные байты, а отправлять только данные, то все пакеты проходят, однако, там байты тоже сдвигаются, и, отправляя, например, картинку, в stm я вижу эту картинку, но видно, что в некоторых местах байты не на месте

CODE
void usart3_init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 9600;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
HAL_UART_Init(&huart3);
}

...

void interrupts_usart3_init(void)
{
HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);

HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 6, 1);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);

HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

__HAL_UART_FLUSH_DRREGISTER(&huart3);
HAL_UART_Receive_DMA(&huart3, &rxBuffer, 1);
}

...

void dma_usart3_rx_init(void)
{
hdma_usart3_rx.Instance = DMA1_Stream1;
hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_rx.Init.MemInc = DMA_MINC_DISABLE;
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_usart3_rx);

__HAL_LINKDMA(&huart3, hdmarx, hdma_usart3_rx);

}

...

void DMA1_Stream1_IRQHandler(void)
{
HAL_NVIC_ClearPendingIRQ(DMA1_Stream1_IRQn);
HAL_DMA_IRQHandler(&hdma_usart3_rx);
}

void USART3_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart3);
}

...

#define MAXCLISTRING 1029

uint8_t rxString[MAXCLISTRING];
uint8_t rxBuffer = 0;
uint8_t rxindex = 0;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_UART_FLUSH_DRREGISTER(&huart3); // Clear the buffer to prevent overrun

if (rxindex == MAXCLISTRING) // если конец пакета
{
rxindex = 0;
if (checkCRC16(&rxString[3], SIZE) == 1) // сравниваю контрольную сумму
{
led_toggle(BLUE);
xbuf[0] = ACK;

UINT* bw;
f_write(&fil, &rxString[3], SIZE, (UINT*)&bw);
if (bw > 0) led_toggle(ORANGE);
HAL_UART_Transmit(&huart3, xbuf, 1, 5); // сообщаю, что готов принять следующий пакет
}
else
{
xbuf[0] = NAK;
HAL_UART_Transmit(&huart3, xbuf, 1, 5); // прошу повторить пакет
}
}
else
{
rxString[rxindex] = rxBuffer; // записываю новый байт в буфер
rxindex++; // увеличиваю индекс
}
}
psL
uart имеет особенность принимать мусор, поэтому принимать решение о начале или конце кадра нужно не по превышению ожидаемого размера, а по признаку начала или конца кадра в данных кадра. т.е. в вашем случае необходимо добавить условие старта кадра при приеме soh от передатчика и только после этого начинать складывать данные в буфер.

Код
UINT* bw;
f_write(&fil, &rxString[3], SIZE, (UINT*)&bw);

звездочка в объявлении лишняя
Klaxons
Спасибо за ваш комментарий. Я вот думаю, что правильней было бы проверить первые 3 символа (SOH, blocknumber и ~blocknumber), тогда я точно могу знать, что это начало фрейма, но в callback приходит по одному байту, это мне надо временный буфер создавать и в него помещать последовательность после SOH?

Еще я обратил внимание, что когда приходит неправильный пакет, то в начало помещаются n количество байтов, которые должны быть в конце пакета (чаще всего начиная с CRC)

И так не понятно, почему в отладчике при брейкпоинте мне удавалось ловить фреймы в правильной последовательности, а без брейкпоинтов, программа получала только первый фрейм корректно, и начиная со второго ни разу не получила фрейм правильно
psL
1) можно использовать существующий rxString
2) можно использовать автомат состояния в приемнике. т.е. должны быть состояния приема soh, проверки номера кадра, проверки инверсии номера, и только после корректного прохода автоматом состояний должна быть фаза "сложить в буфер"
3) можно складывать в буфер в прерывании, а разбирать в отдельном потоке
и тд

Как уже писал ранее смещение кадра м.б. из-за принятого в паузе мусора, при этом голова кадра сместиться от начала буфера. При превышении длины буфера crc не совпадет, т.к. конец кадра определился неправильно. После чего остаток кадра (crc и т.д.) запишется в начало буфера. как-то так.
Tarbal
Если у вас есть длина пакета и контрольная сумма тоже есть, то больше ничего и не надо.

При приеме считаете длину пакета, перезапуская таймаут после каждого принятого байта (с DMA не работает). По таймаутu переводите стейтмашину приема в ожидание длины пакета. По приему байта досчитываете контрольнуя сумму.
Когда все данные пакета приняты сравниваете контрольнуя сумму. Если совпала, то обрабатываете. По приему пакета переводите стейтмашину приема в ожидание длины пакета.

Никаких проблем с синхронизацией не возникнет. Про потере связи -- прерываете коммуникацию на время большее чем таймаут.
Таймаут в нескольло раз превышает время передачи байта скажем для скорости 9600 3 миллисекунды прекрасно работает. Увеличение таймаута удлинит ваш цикл сброса, что никому не надо.
Klaxons
Спасибо большое, все получилось!

А подскажите, может знаете как ускорить передачу файла, может другие протоколы посоветуете? Вот файл в 256 Кб передавался 15 минут, думаю, что как-то ускорить смогу за счет повышения бода у уарта, сейчас передаю по блютуз, 9600 бод (китайский блютуз, который не дает выполнять AT команды)
psL
По идее xmodem не обладает какой-то сверхизбыточностью.
Пропускная способность канала при 9600 это ~1 кБайт в секунду. Т.е. 256кБ передадутся не менее чем за 4,5 минуты. При 15 минутах эффективная скорость 30% от максимальной. Это м.б. связано с серьезными потерями или искажениями данных в канале, но также м.б. что скорость передачи в радиоканале bluetooth меньше 9600
Нужно смотреть на стороне передатчика время между началом передачи кадра и ответом от приемника, вести подсчет ack и nak. Это должно помочь определить узкое место.
Klaxons
Ну вот передатчик говорит 2с на кадр, но все же думаю, что передатчик подтупливает. Настройку у библиотеки передатчика baud rate не нашел, думал она автоматически подстраивается, но нет. Видимо на малой скорости передает, а скорость у ресивера поднял до 115200, но по ощущениям, что скорость потока не изменилась. В общем буду думать, по потерям сложно сказать, судя по дебагеру, микроконтроллер не просит повторить пакет, все приходит с первого раза
Ruslan1
Klaxons, я бы еще по частям проверил, чтобы понять где узко и что достижимо:
Тест#1: соединил два компьютера кабелем (нульмодем), проверил скорость работы
Тест#2: добавил в канал блютуз.
Тест#3: заменил один компьютер на МК.
Можно поменять местами тесты 2 и 3.


Я как-то очень много времени потратил на битву с блютузом, а оказалось, что встроенный в мой делловский ноутбук блютуз плохо уживался с майкрочиповским модулем блютуза. Подключил к компу внешний самый простой USB блютуз- и проблема исчезла. Так что засады бывают и совсем не с той стороны где их ищут.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.