У меня есть микроконтроллер TM4C123GXL Launchpad с 8 последовательными портами UART. Я использую Keil uVision5 с Tiva Ware для программирования.
Я хотел соединить 3 устройства UART с микроконтроллером без потери байтов. Скорость передачи установлена на 115200 бит/с без контроля четности, как показано на рисунке ниже. Все устройства периодически отправляют фрейм данных каждые 5 мс.
Время TX и RX рассчитывается с использованием No_0f_bytes*10/BaudRate.
Я успешно соединил устройства с UART µC. Проблема в связи в том, что я теряю байты. Если я устанавливаю связь с одним устройством (Устройство 2), я все равно теряю байты всего кадра (20 байт).
Это связано с 16-байтовым ограничением FIFO Tm4c123 или чем-то еще?
Я также реализовал функцию µDMA TM4C123. Но все равно байты теряются. Что я могу сделать, чтобы улучшить систему для передачи и приема кадров данных без потерь?
Редактировать:
Вот архитектура программного обеспечения:
Я использую периодическое прерывание таймера 5 мс для приема и передачи кадра. Весь кадр имеет первые 2 байта заголовка и байт счетчика в качестве последнего байта.
void Timer1IntHandler(void) // Periodic Service Routine every 5ms
{
DisableIntrupts();
bool Correct=ReadJoystick(); //10 bytes Device 1
if(Correct)
{
GenerateServoCardsFrame();
SendServo1Frame(); //20 bytes Device 2
SendServo2Frame(); //17 bytes Device 3
ReadServo1Frame(); //15 bytes Device 2
ReadServo2Frame(); //20 bytes Device 3
GenerateJoystickFrame();
SendJoystickFrame(); //10 bytes Device 1
EnableIntrupts();
}
}
main()
{
SetupClock() ; //Setup 16 MHz Clock
SetupJoystick(); //Initalize uart1 port for Device1
SetupServoCard1(); //Initalize uart2 port for Device2
SetupServoCard2(); //Initalize uart3 port for Device3
InitalizePeriodicTimerHandler(5); //Periodic Service Routine every 5ms (Timer1IntHandler)
while(1)
{
}
}
bool ReadJoystick(void)
{
int BytePos=0;
int CountInvalid=0;
int LoopoutTime=0;
while(1)
{
if (ROM_UARTCharsAvail(UART1))
{
ByteRX = ROM_UARTCharGetNonBlocking(UART1);
if (BytePos==0)
{
if (ByteRX== 0xA1) //Header1 found
{
KArray[0] = Bytebuf ;
BytePos ++;
}
else
{
CountInvalid++;
if (CountInvalid>5)
return 0;
}
}
else if (BytePos ==1)
{
if (ByteRX == 0x66) //Header2 found
{
KArray[1] = ByteRX;
BytePos ++;
}
else
BytePos=0;
}
else
{
KArray[BytePos++] = ByteRX;
if (BytePos==10)
return 1; //Frame Recived
}
}
else
{
SysCtlDelay(0.25*SysCtlClockGet()/3 / 1000); // 0.25ms delay
LoopoutTime++;
if (LoopoutTime > 10)
return 0;
}
}
}
Согласно моим расчетам, 1 байт требует 10/115200 = 0,08680 мс, а для чтения FIFO из 16 байт требуется 1,38 мс. В таблице на рисунке показано общее время передачи 4,08 мс и время приема 3,91 мс, что в сумме составляет 8 мс. Это больше, чем моя программа обслуживания периодических прерываний.
Нужно ли увеличивать время периодического прерывания?
Дизайн вашего программного обеспечения не очень хорош и, вероятно, является причиной потери входящих байтов.
Во-первых, я не уверен, ошибка это или опечатка. Вы отключаете прерывания в начале Timer1IntHandler()
, но затем вы снова разрешаете прерывания только в том случае, если Correct
это правда. Разве вы не хотите повторно разрешать прерывания перед возвратом независимо от условного? Кажется странным, что прерывания можно оставить отключенными, когда функция возвращается.
Похоже, ваш код читает символы из UART1 только внутри ReadJoystick()
функции. И я подозреваю, что UART1 не читается, пока вызываются все эти функции от GenerateServoCardsFrame()
до . SendJoystickFrame()
Сколько времени требуется для запуска этих функций? Могут ли эти функции занять достаточно много времени, чтобы FIFO UART1 заполнился и переполнился? Это может быть, когда входящие байты отбрасываются.
Если бы я разрабатывал это программное обеспечение, я бы реализовал его совершенно иначе, чем то, что сделали вы. Я бы включил запрос прерывания UART и создал бы быструю процедуру обработки прерывания UART. Единственное, что будет делать UART ISR, это копировать байты в/из регистров UART TX/RX. Я бы создал два круговых (кольцевых) буфера для хранения байтов. UART ISR скопирует полученный байт из регистра RX UART в циклический буфер RX. И UART ISR скопирует байт для передачи из циклического буфера TX в регистр TX UART. UART ISR не будет пытаться интерпретировать значение любого из байтов. Все, что он делает, это перемещает байты между буферами RAM и периферийным устройством UART. Это делает ISR UART коротким, что позволяет программе в целом лучше реагировать на другие прерывания.
Затем я бы создал main()
функцию с бесконечным циклом, и внутри бесконечного цикла я бы вызвал функцию, вызываемую SerialReceive()
для чтения сообщений из буфера RX. SerialReceive()
будет реализован как конечный автомат. Если какие-либо байты доступны в буфере RX, то он будет обрабатывать конечное число байтов через конечный автомат. Конечный автомат будет иметь состояния для заголовка кадра, тела и трейлера, подобные тому, что вы сделали. SerialReceive()
немедленно возвращается, когда либо сообщение завершено, либо больше нет доступных байтов. Когда сообщение является неполным из-за того, что больше нет доступных байтов, SerialReceive()
оно не будет ждать их, вместо этого оно запомнит текущее состояние и сообщение, чтобы продолжить с тем же сообщением при повторном вызове из main()
.
Если вам нужно что-то делать периодически, настройте таймер, как вы сделали, но вместо того, чтобы выполнять всю работу в ISR таймера, просто вместо этого установите флаг. Основной бесконечный цикл должен неоднократно проверять флаг и делать все необходимое, когда флаг установлен ISR таймера. Выполнение работы из контекста означает main()
, что система может реагировать на другие прерывания во время выполнения работы.
Короткие ISR позволяют системе в целом лучше реагировать на другие запросы прерывания. Если вы проводите слишком много времени в ISR, как я полагаю, вы делаете в своем ISR с таймером, тогда система не будет реагировать.
Обновление: в своем комментарии вы говорите, что эти функции зацикливаются до завершения передачи, и они занимают более 7 миллисекунд. Этого времени достаточно, чтобы 80 байт поступили на UART, и ваш код не считывает эти байты в течение этого времени, поэтому, конечно, байты будут потеряны.
Ваши передающие функции должны копировать байты в буфер TX и возвращаться, не дожидаясь передачи всего сообщения. UART ISR должен передавать один байт при каждом вызове, пока буфер TX содержит байты.
Буфер RX и TX должен быть больше любого сообщения. Обычно размер буферов равен степени двойки, потому что это позволяет легко обвести указатели буферов назад к началу. Так что сделайте их 256 байт (или 128 или 64, но почему бы не больше?).
У вас должен быть независимый набор буферов RX/TX для каждого UART.
Изменение периода вашего периодического таймера ISR не повлияет на проблему с вашим исходным кодом. В вашем периодическом ISR ваш код тратит 7 миллисекунд, НЕ читая UART. Ваш код потеряет байты независимо от периода таймера.
Миту Радж
Масуд Салик
Миту Радж
Масуд Салик
Миту Радж
ккрамбо
Миту Радж
придурок
придурок
придурок
Масуд Салик
придурок
Масуд Салик
придурок
придурок
придурок
Лундин
volatile
ошибки повсюду. Формаmain
used относится к эпохе динозавров, что говорит о том, что используется плохой компилятор. И так далее.Масуд Салик
придурок
Масуд Салик
придурок
придурок