Прерывание UART не получает более одного символа — PIC32MX110F106B

Я действительно борюсь со связью UART с моим PIC32MX110F016 . Это первый раз, когда я пытаюсь реализовать связь UART с семейством PIC32MX, но мне это сложно, и я не знаю, почему это не работает должным образом. Поэтому я решил опубликовать весь свой код в виде учебника (шаг за шагом), чтобы получить помощь от вас, ребята. Я надеюсь, что это поможет в будущем другим людям, столкнувшимся с той же проблемой.

Итак, сначала я импортировал библиотеки и объявил конфиги этого микроконтроллера:

#pragma config   FNOSC       = FRCPLL
#pragma config   FPLLIDIV    = DIV_2
#pragma config   FPLLMUL     = MUL_20
#pragma config   FPLLODIV    = DIV_2
#pragma config   FPBDIV      = DIV_1
#pragma config   ICESEL      = ICS_PGx2
#pragma config   WDTPS       = PS16384

#define GetSystemClock()        (40000000ul)
#define GetPeripheralClock()    (GetSystemClock())
#define BaudRate   115200

Затем я реализовал свою функцию uartconfig():

void uartconfig(void){
PPSUnLock;                        // Allow PIN Mapping for BLE
  PPSOutput(1, RPA0, U1TX);     // MAP Tx to RA0 set to digital out
  PPSInput (3, U1RX, RPA2);     // MAP Rx to RA2 set to digital in
  PPSOutput(4, RPA3, U1RTS);
  PPSInput (2, U1CTS,RPA1);
PPSLock;                         // Prevent Accidental Mapping

#define UART1TX TRISAbits.TRISA0
#define UART1RX TRISAbits.TRISA2

#define CMD TRISBbits.TRISB5

UART1TX = 0;//output
UART1RX = 1;//input

DDPCONbits.JTAGEN = 0;

UARTConfigure(UART1, UART_ENABLE_PINS_CTS_RTS);
UARTSetFifoMode(UART1, UART_INTERRUPT_ON_TX_NOT_FULL | UART_INTERRUPT_ON_RX_NOT_EMPTY);
UARTSetLineControl(UART1, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
UARTSetDataRate(UART1, GetPeripheralClock(), BaudRate);
UARTEnable(UART1, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));

INTEnable(INT_SOURCE_UART_RX(UART1), INT_ENABLED);
INTSetVectorPriority(INT_VECTOR_UART(UART1), INT_PRIORITY_LEVEL_2);
INTSetVectorSubPriority(INT_VECTOR_UART(UART1), INT_SUB_PRIORITY_LEVEL_0);

// configure for multi-vectored mode
INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
INTEnableInterrupts();
}

Затем я решил создать command_printфункцию для отправки данных через UART:

void command_print(char *buffer){ 
while(*buffer != (char)0)
{
  while(!UARTTransmitterIsReady(UART1));
  UARTSendDataByte(UART1, *buffer++);
}
UARTSendDataByte(UART1, '\r');
while(!UARTTransmissionHasCompleted(UART1));
}

Я решил создать структуру, чтобы создать буфер для вывода TX/RX (теперь я просто использую inputBuffer):

typedef volatile struct UARTBuffer {
char outputBuffer[ UART_INPUT_BUFFER_SIZE ];
char inputBuffer[ UART_OUTPUT_BUFFER_SIZE ];
unsigned short int inputReaderPos;
unsigned short int inputWriterPos;
unsigned short int outputReaderPos;
unsigned short int outputWriterPos;
} UARTBuffer;

UARTBuffer UARTBuffer1;

В своей подпрограмме обслуживания прерываний ISRя решил игнорировать эту U1TXчасть и всегда сбрасывать флаг. Для U1RXя определил как:

void __ISR(_UART_1_VECTOR, ipl2)UART1HANDLER(void){
char c;
if ( INTGetFlag(INT_U1TX) )
 {
    INTClearFlag(INT_SOURCE_UART_TX(UART1));
}

if ( INTGetFlag(INT_U1RX) )
 {

if( (U1STAbits.OERR == 1) || (U1STAbits.PERR == 1) || (U1STAbits.FERR == 1) ){
    // Ignore
    U1STAbits.OERR = 0;//clears if the Receive buffer has overflowed
    U1STAbits.PERR = 0;//parity error
    U1STAbits.FERR = 0;//framing error
    U1RXREG;
}
else {
    // Check if the buffer is all readed. If so, clear the buffer;
    if( UARTBuffer1.inputWriterPos == UARTBuffer1.inputReaderPos ) {
        UARTBuffer1.inputWriterPos = 0;
        UARTBuffer1.inputReaderPos = 0;
    }
    if (UARTBuffer1.inputWriterPos >= UART_INPUT_BUFFER_SIZE){
        // Buffer overflow
        UARTBuffer1.inputWriterPos = 0;
        UARTBuffer1.inputReaderPos = 0;
    }
    c = U1RXREG;
    UARTBuffer1.inputBuffer[ UARTBuffer1.inputWriterPos++ ] = c;
}
INTClearFlag(INT_SOURCE_UART_RX(UART1));
}
}

Наконец, my main(void)определяется как:

int32_t main(void) {
__asm__("EI");
UARTBuffer1.inputWriterPos = 0;
SYSTEMConfig(GetSystemClock(), SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

INTEnableSystemMultiVectoredInt(); //enable interrupts

uartconfig();

command_print("SF,1");
while (1);
}

На самом деле мой PIC работает нормально, и мое прерывание срабатывает. Что я не могу понять, так это причину, по которой мой код никогда не получает более одного символа в части RXReg(а именно U1RXREG) внутри ISR. Я сбрасываю флаг к концу ISR и думаю, что все настроено правильно. Дайте мне знать, что я должен сделать, чтобы правильно получить свой кадр, а не только первый символ. Я знаю, что UART хорошо спроектирован, и если я попытаюсь отправить другую команду, "SF,1"регистр RX даст мне другой символ.

РЕДАКТИРОВАТЬ: я не достигаю того, чего хочу. Я теряю символы в прерывании, поэтому подключаю CTS+ RTS, но, к сожалению, не получаю лучших результатов. Кто-нибудь может помочь мне с конфигурацией CTS+ RTSдля семейства PIC32mx? Я знаю, что мне нужно скрестить CTS с RTS и RTS с CTS от микроконтроллера к «другому» компоненту. Моя проблема больше связана с функциями/конфигурациями, которые мне нужно делать.

Вы читаете один символ за раз из U1RXREG, я думаю, вам может сойти с рук сохранение всего U1RXREG в вашей переменной за один раз.
Я не уверен в этом. Я уже заглянул в U1RXREG, и у него просто значение 0x00000041, и это неприемлемый результат.
0x41 — это «А». Кого вы ожидали увидеть в качестве второго персонажа?
«AOK\r\n» - это то, что я пытаюсь получить.
Определенно следует сделать эту структуру UartBuffer1 «изменчивой».
@ Просто это хороший совет, и вы правы насчет использования volatile структур внутри прерывания, но в данном случае это не решает проблему. Обратите внимание, у меня просто 0x00000041 в качестве значения U1RXREG.
Работает ли он на более низких скоростях передачи данных? Будет ли он вести себя лучше, если вы очистите флаг прерывания сразу после его проверки?
Нет. Ни одно из ваших предложений не подействовало.

Ответы (1)

При скорости 115200 бод, без контроля четности и с 1 стартовым и стоповым битом (всего 10 бит) требуется 86 микросекунд, чтобы данные поступили в ваш RX-буфер с другого конца.

Глядя на ваш ISR, я вижу, что вы обрабатываете много инструкций в своем ISR.

Сценарий, который может произойти, таков: появляется ваш первый персонаж (т.е. «А»),

Прерывание срабатывает,

ошибок не было (переполнение, четность и т.д.),

теперь приходят и остальные символы ("ОК\r\n"),

устанавливается флаг переполнения,

вы очищаете прерывание RX (но вы не вводите ISR снова, потому что больше не будут получены символы).

Решения по отладке: 1. Используйте метод управления потоком (ACK для каждого байта).

  1. Проверьте размер Rx FIFO вашего UC (если его 16 байт, вам не нужно управление потоком. В противном случае, если нет FIFO, вам нужно управление потоком)

3. Избегайте вызовов функций в ISR.

4. Вы можете включить прерывание для ошибок переполнения, чтобы вам не нужно было явно писать процедуру для прерывания. Если возникает ошибка переполнения, просто проверьте состояние флага прерывания, очистите флаг и отбросьте данные Rx. Это поможет вам сократить обработку таких ошибок в ISR, поскольку их можно проверить в самом основном цикле. Просто скопируйте полученные данные в буфер и увеличьте счетчик. Это может помочь.

  1. Уменьшите скорость передачи данных и проверьте, используя тот же код
Тактовая частота ЦП составляет 40 МГц, а скорость передачи данных — 115200 бит/с. Каждый символ имеет 8 бит данных, стоповый бит и стартовый бит, всего 10 бит на символ. Следовательно, у вас есть 40 МГц / 115200 бит/с * 10 бит на символ = 3472 цикла ЦП для обработки каждого символа до поступления следующего. Поэтому маловероятно, что ваш ISR слишком медленный для обработки символов по мере их поступления, поэтому, если вы не отключите прерывания где-то еще, вы вряд ли переполнитесь.
Уже пробовал со скоростью 19200 бод и никаких лучших результатов.