Перейти к содержанию
    

PIC18F25K22. USART. Прерывание по передаче данных

Добрый день. Есть контроллер серии PIC18. Возникла сейчас необходимость производить передачу по USART в прерывании. Раньше для этого я использовал функцию, и работало все без прерываний:

void usart_Tx(void* buf,BYTE size)
{
    BYTE i,j;
    UINT16 k;
    char* ptr = (char*)buf;
    TRAN; // установить признак передачи для переключения переходника TTL-RS485
    PIE1bits.RCIE = 0; // запретить прерывание от приемника USART
    for(i = 0; i < size; i++)
    {
        TXREG1 = ptr[i]; // запись байта в буфер передатчика USART
        while(!PIR1bits.TXIF); // ожидание опустошения буфера
        for(j = 0; j < 255; j++) // временная выдержка для достижения скорости передачи
            k++;
    }
    REC; // установить признак приема для переключения переходника TTL-RS485
    PIE1bits.RCIE = 1;    // разрешить прерывание от приемника USART
}

Нужно именно использовать прерывания, чтобы контроллер не отвлекался надолго на передачу. Но что-то не могу найти хотя бы одного примера, как это организовывается.Все, что придумал, это
 

char* ptr = (char*)usart_rx_buf;

// количество данных для передачи
void tx_str(BYTE cnt)
{
    
    PIE1bits.TXIE = 1;                       
    TRAN; // установить признак передачи для переключения переходника TTL-RS485
    PIE1bits.RCIE = 0; // запретить прерывание от приемника USART
    
    totalElementstoSend = cnt;
    sendPointer = 0;
    
    REC; // установить признак приема для переключения переходника TTL-RS485
    PIE1bits.RCIE = 1;    // разрешить прерывание от приемника USART
}

void usart_Tx_Int(void)
{
    BYTE i,j;
    UINT16 k;

    TXREG = ptr[sendPointer]; 
    while(!PIR1bits.TXIF); // ожидание опустошения буфера
        for(j = 0; j < 255; j++) // временная выдержка для достижения скорости передачи
            k++;
    sendPointer++;
    if (sendPointer >= totalElementstoSend)
        PIE1bits.TXIE = 0; 
    PIR1bits.TXIF = 0;
}


 

Вроде все передается, но все равно как будто не то. Подскажите, пожалуйста, как это сделать лучше? Или может у кого небольшой фрагментик готового кода завалялся  :)) 

Изменено пользователем Auratos

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как бы смысла в такой реализации нет...

TXREG = ptr[sendPointer];
while(!PIR1bits.TXIF); // ожидание опустошения буфера

Вы в прерывании по завершению передачи дожидаетесь завершения передачи.

Да и вот это тоже:

 for(j = 0; j < 255; j++) // временная выдержка для достижения скорости передачи
            k++;

Если нужна задержка между байтами,  в этом случае лучше использовать таймер


Должно быть как-то так:
 - Сохраняете кол-во отправляемых байт
 - Включаете  прерывание по завершению передачи
 - Отправляете первый байт
 - В обработчике завершения прерывания отправляете следующий байт
 - Как только все отправили отключаем прерывание, включаем режим приема.

И помните, до момента завершения отправки, в вашей реализации, буфер нельзя модифицировать

Соблюдая Ваш код стайл, возможная реализация:

void tx_str(BYTE cnt)
{  
    //здесь не мешало бы проверить закончилась ли предыдущая транзакиця
    //проверяя кол-во отправленых байт или REC или TRAN
  
    totalElementstoSend = cnt;
    sendPointer = 1;
    
    PIR1bits.TXIF = 0;
    PIE1bits.TXIE = 1;                       
    TRAN; // установить признак передачи для переключения переходника TTL-RS485
    PIE1bits.RCIE = 0; // запретить прерывание от приемника USART
    
    TXREG = ptr[0];      
}

void usart_Tx_Int(void)
{
    BYTE i,j;
    UINT16 k;

    PIR1bits.TXIF = 0;
    if (sendPointer < totalElementstoSend) 
    {
        TXREG = ptr[sendPointer];       
        sendPointer++;
        return;
    }

    PIE1bits.TXIE = 0; 
    REC; // установить признак приема для переключения переходника TTL-RS485
    PIE1bits.RCIE = 1;    // разрешить прерывание от приемника USART    
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

57 minutes ago, Integro said:


Соблюдая Ваш код стайл, возможная реализация:


if (sendPointer < totalElementstoSend) 
{
  TXREG = ptr[sendPointer];       
  sendPointer++;
  return;
}

 

Попробовал ваш вариант. Контроллер отвечает шустро, только ответ неверный на запрос. Вместо правильного ответа

[01][03][0C][00][00][00][00][00][00][00][00][00][05][00][00][83][71]

приходит

[03][01][00][00][00][00][00][00][00][00][00][05][00][00][83][FD]

Не подскажите, в чем может быть загвоздка?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 minutes ago, Auratos said:

Попробовал ваш вариант. Контроллер отвечает шустро, только ответ неверный на запрос. Вместо правильного ответа

[01][03][0C][00][00][00][00][00][00][00][00][00][05][00][00][83][71]

приходит

[03][01][00][00][00][00][00][00][00][00][00][05][00][00][83][FD] 

Не подскажите, в чем может быть загвоздка?

 

Странно что первый и второй байт поменялись местами. А потерялся третий)
Из-за того что испорчены начало и конец пакета, можно предположить что на переключение режимов трансивера(TRAN и REC) нужно больше времени, по этому после TRAN и перед REC нужна задержка. Но задержка в прерывании... мягко говоря так нельзя делать. На данном этапе можно убедиться в этом ли проблема, а потом думать как поступить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, Integro сказал:

Странно что первый и второй байт поменялись местами. А потерялся третий)

Вы сначала разрешаете прерывание от TX, оно немедленно происходит, обработчик записывает в регистр данных передатчика ptr[1], флаг TXnIF сбрасывается.  Из регистра данных передатчика байт сразу же загружается в сдвиговый регистр, регистр данных теперь свободен, флаг TXnIF снова взводится.

После выхода из обработчика немедленно происходит новое прерывание. Теперь в передатчик записывается ptr[2], флаг TXnIF сбрасывается. Байт остается в буферном регистре поскольку сдвиговый регистр пока занят.

После возврата из второго прерывания выполняется TXREG = ptr[0]; а записанный только что ptr[2] теряется.

Вот байты и поменялись местами, а ptr[2] потерялся.

 

Флаги TXnIF и RCnIF только на чтение (read-only), писать в них что-либо бесполезно, они сами сбрасываются после записи в передатчик или чтения из приемника.

 

В регистре  TxSTAx есть флаг TRMT - Transmit Shift Register Status bit, сигнализирующий что сдвиговый регистр передатчика пуст. Но прерываний для него не предусмотрено, поэтому ожидать окончания передачи последнего байта пакета придется или программно опрашивая этот флаг или придумать что-то ещё.

 

Изменено пользователем SSerge
Уточнил сценарий перепутывания и потери байт при передаче.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, SSerge said:

Флаги TXnIF и RCnIF только на чтение (read-only), писать в них что-либо бесполезно, они сами сбрасываются после записи в передатчик или чтения из приемника.

Да, точно! Забыл... Спасибо что поправили!

Тогда должен работать такой вариант:

void tx_str(BYTE cnt)
{  
    //здесь не мешало бы проверить закончилась ли предыдущая транзакиця
    //проверяя кол-во отправленых байт или REC или TRAN
  
    PIE1bits.RCIE = 0; // запретить прерывание от приемника USART
    TRAN; // установить признак передачи для переключения переходника TTL-RS485
    
    totalElementstoSend = cnt;
    sendPointer = 1;
                          
    TXREG = ptr[0];  
    PIE1bits.TXIE = 1;     
}

Но проблему с последним байтом это не решает, можно сделать так, но это решение блокирует все прерывания пока отправляется последний байт...

void usart_Tx_Int(void)
{
    BYTE i,j;
    UINT16 k;

    PIR1bits.TXIF = 0;
    if (sendPointer < totalElementstoSend) 
    {
        TXREG = ptr[sendPointer];       
        sendPointer++;
        return;
    }

    while(!TXSTA1bits.TRMT); //Так делать нельзя, если конечно не ожидаете другие прерывания!!!

    PIE1bits.TXIE = 0; 
    REC; // установить признак приема для переключения переходника TTL-RS485
    PIE1bits.RCIE = 1;    // разрешить прерывание от приемника USART    
}

Либо, можно запустить таймер на время соответствующее передаче двум байтам, и уже там переключить rs485 на прием
Либо, блокировать отправку ожиданием индекса и флага в main, но тогда нет смысла от использования прерываний.
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 hours ago, Integro said:

Тогда должен работать такой вариант

 

12 hours ago, SSerge said:

Вот байты и поменялись местами, а ptr[2] потерялся.

Спасибо большое вам за помощь. Все работает :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...