Обсуждаем контроллеры компании Atmel.
Ответить

ATtiny441 и UART. Не могу разобраться

Ср июл 03, 2019 19:40:35

Коллеги, помогите!

Появилось у меня хобби - начал с Ардуино UNO и теперь дорос до контроллера ATtiny441 + Atmel Studio 7. Играюсь с домашне-дачной автоматизацией ...

Задача очень простая: удаленно посылая телеграммы по RS-485 (протокол ModBus RTU) управлять с компьютера периферией и отвечать компьютеру о своем состоянии. Начал писать код:

Код:
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
#define BUFFER_SIZE 128
#define StartTransmit_SET_OUTPUT  DDRA  |=  (1 << 0)
#define StartTransmit_LOW  PORTA |=  (1 << 0)
#define StartTransmit_HIGH  PORTA &= ~(1 << 0)
 
#define LED_SET_OUTPUT  DDRA  |=  (1 << 4)
#define LED_LOW  PORTA |=  (1 << 4)
#define LED_HIGH  PORTA &= ~(1 << 4)
 
volatile unsigned char slaveID = 50;
unsigned char frame[BUFFER_SIZE];
unsigned char funktion=1;
bool broadcastFlag=0;
 
void USART_Init()
{
    UBRR0H = 0;
    UBRR0L = 6;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
 
unsigned int calculateCRC(uint8_t bufferSize)
{
    unsigned int temp, temp2, flag;
    temp = 0xFFFF;
    for (unsigned char i = 0; i < bufferSize; i++)
    {
        temp = temp ^ frame[i];
        for (unsigned char j = 1; j <= 8; j++)
        {
            flag = temp & 0x0001;
            temp >>= 1;
            if (flag)
            temp ^= 0xA001;
        }
    }
    temp2 = temp >> 8;
    temp = (temp << 8) | temp2;
    temp &= 0xFFFF;
    return temp;
}
 
void USART_Transmit( unsigned char data )
{
    StartTransmit_LOW;
        _delay_ms(5);
            while ( !( UCSR0A & (1<<UDRE0)) );
            UDR0 = data;
        _delay_ms(5);
    StartTransmit_HIGH;
    _delay_ms(5);
}
 
void USART_Transmit_String()
{
    int i=0;
    unsigned char c = frame[0];
            while(c != '\0')
                {
                    c = frame[i];
                        if(c!='\0') {USART_Transmit(c);}
                    i++;
                }
}
 
void exceptionResponse(unsigned char exception)
{
    if (!broadcastFlag)
    {
        frame[0] = slaveID;
        frame[1] = (funktion | 0x80);
        frame[2] = exception;
        unsigned int crc16 = calculateCRC(3);
        frame[3] = crc16 >> 8;
        frame[4] = crc16 & 0xFF;
        USART_Transmit_String();
    }
}
 
ISR (USART0_RX_vect)
{
        while ( !(UCSR0A & (1<<RXC0)) );
        slaveID = UDR0;
}
 
int main(void)
{
    USART_Init();
    StartTransmit_SET_OUTPUT;
    LED_SET_OUTPUT;
    LED_LOW;
 
    sei();
    while(1)
    {
                exceptionResponse(2);
                _delay_ms(5000);           
    }
}

и столкнулся со следующей хренью: вбиваю в Terminal Window (встроенный терминал в Atmel Studio 7) число 33 - по указанному выше алгоритму если в буфере что то появилось, то переменная slaveID становится этим. Но она этим становится в хаотичном порядке: может так: 33 81 02 60 5e - и это правильно, она 33, а может так: b3 81 02 61 b6 - это когда первый бит у тройки заменен на 1. Причем непонятно с чем это связано. Вроде вбиваю в поле Send терминала 33, все честно, но результат на выходе ИЛИ-ИЛИ. Нет стабильности. Например та же хрень при вводе 54: 5 раз d4 81 02 d0 69, три раза 54 81 02 d1 81 Для приема в терминал использую китайский переходник с UART на RS-485 и переходник - RS-485 - USB. Если есть у кого какие мысли - помогите, пожалуйста, разобраться.

Спасибо!

Re: ATtiny441 и UART. Не могу разобраться

Ср июл 03, 2019 20:11:57

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

Re: ATtiny441 и UART. Не могу разобраться

Ср июл 03, 2019 20:33:23

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


Все просто (если вкрадце и отбросить все лишнее):
Тут инициализация UART: void USART_Init() - 9600/8/2
считаем CRC: unsigned int calculateCRC(uint8_t bufferSize)
отправляем символ: void USART_Transmit( unsigned char data )
отправляем строку (посимвольно): void USART_Transmit_String()
формируем телеграмму из 5 символов: void exceptionResponse(unsigned char exception)
читаем из буфера UART по прерыванию: ISR (USART0_RX_vect)
основная программа. int main(void) - шлет в порт телеграмму раз в 5 секунд. Если я в порт пишу значение (например 33), то принимаю его как 33 то как b3 ...

Контроллер настроен на внутренний осциллятор и на частоту 1 МГц.

Re: ATtiny441 и UART. Не могу разобраться

Ср июл 03, 2019 23:01:13

А чего у вас frame[5] нулём не инициализируется?

Вы только одной программой пробовали данные принимать?

Я правильно понял, что вы принимаете с компьютера байт, записываете его в slaveID, а затем отправляете назад в сложной посылке? Если да, то именно это я и имел в виду под словами "каким принципом руководствуется программа". Содержание процедур было понятно из их названий (и это, кстати, хорошо), так что ваш ответ особой ясности не внёс.

Re: ATtiny441 и UART. Не могу разобраться

Чт июл 04, 2019 15:00:27

Решено!

правильно инициализировать UART в моем случае вот так:

void USART_Init()
{
UBRR0H = 0;
UBRR0L = 12;

UCSR0A = (1 << U2X0);

UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}

Ошибок больше не ловил!
Ответить