Jump to content

    

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;
}


 

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

Edited by Auratos

Share this post


Link to post
Share on other sites

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

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    
}

 

Share this post


Link to post
Share on other sites
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]

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

Share this post


Link to post
Share on other sites
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 нужна задержка. Но задержка в прерывании... мягко говоря так нельзя делать. На данном этапе можно убедиться в этом ли проблема, а потом думать как поступить.

Share this post


Link to post
Share on other sites
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, сигнализирующий что сдвиговый регистр передатчика пуст. Но прерываний для него не предусмотрено, поэтому ожидать окончания передачи последнего байта пакета придется или программно опрашивая этот флаг или придумать что-то ещё.

 

Edited by SSerge
Уточнил сценарий перепутывания и потери байт при передаче.

Share this post


Link to post
Share on other sites
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, но тогда нет смысла от использования прерываний.
 

Share this post


Link to post
Share on other sites
11 hours ago, Integro said:

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

 

12 hours ago, SSerge said:

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

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this