Произвольная скорость USART (RS-232)

Модератор: Модераторы разделов

Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Произвольная скорость USART

Сообщение eddy »

Собственно вопрос: как можно установить произвольную скорость? Работаю с микроконтроллером по RS-232, из стандартных скоростей у него только ~9600 и ~19200 бод, остальные =250кбод/N, где N - целое. Скорости передачи данных не хватает (нужно успеть передать четыре считанных по SPI-интерфейсу байта за паузу между передачами, ~100мкс). Т.е. здесь очень подошла бы скорость 83.3кбод и выше (хотелось бы даже 250кбод).
В интернете нашел только такой вариант:

Код: Выделить всё

    struct serial_struct nuts, old_extra_term; int RB;
    ioctl(comfd, TIOCGSERIAL, &old_extra_term);
    nuts = old_extra_term;
    nuts.baud_base = Baudrate; // вариант 1
    nuts.custom_divisor = 1; // вариант 1
        // вариант 2:
        // nuts.custom_divisor = nuts.baud_base / Baudrate;
    //if (!(nuts.custom_divisor)) nuts.custom_divisor = 1;
    nuts.flags &= ~ASYNC_SPD_MASK;
    nuts.flags |= ASYNC_SPD_CUST;
    ioctl(comfd, TIOCSSERIAL, &nuts);
    ioctl(comfd, TIOCGSERIAL, &nuts);
    RB = nuts.baud_base / nuts.custom_divisor;
    printf("Target baudrate=%d, real baudrate=%d\n", Baudrate, RB);

Работает только при установке SUID-бита или запуске от рута. printf пишет, что скорость установилась нужная (правда, во втором варианте nuts.baud_base = 115200), однако, данные через канал не идут.
Ошибка точно не со стороны контроллера, т.к. если выставляю таким способом стандартную скорость 9600бод, компьютер контроллер не видит.
Может быть, кто-нибудь сталкивался с такой проблемой?
P.S. Кажется мне, в офтопике можно было задавать произвольный бодрейт, так что проблема не аппаратная.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Произвольная скорость USART

Сообщение NickLion »

Такое чувство, что не получится это сделать. Посмотрел исходники Mono и minicom. В обоих случаях бодрейт выставляется к одним из стандартных. Константы вида B230400. Посмотрите, может есть подходящее среди них (не под линуксом сейчас)? Нашел http://tomoyo.sourceforge.jp/cgi-bin/lxr/s...termbits.h#L146 - там вроде нет подходящего.

PS в винде есть SetCommState (Эту функцию использует .net). В описании структуры тоже накладываются ограничения на бодрейт. Но когда-то организовывал на C# работу с железякой, передавал 9600 - она не успевала, а на 9500 - работала... Т.е. была разница.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Произвольная скорость USART

Сообщение eddy »

Скорость USART рассчитывается по формуле baudrate=baud_base/custom_divisor.
Однако, по умолчанию baud_base = 115200. Если эту величину выставить неправильно, интерфейс не работает. Хотелось бы узнать максимальное допустимое значение baud_base. Теоретически это может быть и 1Мбод, в зависимости от реальной частоты кварца.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
mrcashe
Сообщения: 18

Re: Произвольная скорость USART

Сообщение mrcashe »

eddy писал(а):
01.12.2009 09:14
Собственно вопрос: как можно установить произвольную скорость? Работаю с микроконтроллером по RS-232, из стандартных скоростей у него только ~9600 и ~19200 бод, остальные =250кбод/N, где N - целое. Скорости передачи данных не хватает (нужно успеть передать четыре считанных по SPI-интерфейсу байта за паузу между передачами, ~100мкс). Т.е. здесь очень подошла бы скорость 83.3кбод и выше (хотелось бы даже 250кбод).
В интернете нашел только такой вариант:

Код: Выделить всё

    struct serial_struct nuts, old_extra_term; int RB;
    ioctl(comfd, TIOCGSERIAL, &old_extra_term);
    nuts = old_extra_term;
    nuts.baud_base = Baudrate; // вариант 1
    nuts.custom_divisor = 1; // вариант 1
        // вариант 2:
        // nuts.custom_divisor = nuts.baud_base / Baudrate;
    //if (!(nuts.custom_divisor)) nuts.custom_divisor = 1;
    nuts.flags &= ~ASYNC_SPD_MASK;
    nuts.flags |= ASYNC_SPD_CUST;
    ioctl(comfd, TIOCSSERIAL, &nuts);
    ioctl(comfd, TIOCGSERIAL, &nuts);
    RB = nuts.baud_base / nuts.custom_divisor;
    printf("Target baudrate=%d, real baudrate=%d\n", Baudrate, RB);

Работает только при установке SUID-бита или запуске от рута. printf пишет, что скорость установилась нужная (правда, во втором варианте nuts.baud_base = 115200), однако, данные через канал не идут.
Ошибка точно не со стороны контроллера, т.к. если выставляю таким способом стандартную скорость 9600бод, компьютер контроллер не видит.
Может быть, кто-нибудь сталкивался с такой проблемой?

baud_base устанавливается осью и зависит от параметров микросхемы UART. Стандартный UART 16550A тактируется от кварца
1843200 Гц, 1843200/16=115200, что соответствует обычно максимальной скорость обмена. 16550 устроена так, что частоту кварца она делит на 16. Так что играть можно только при помощи custom_divisor.

eddy писал(а):
01.12.2009 09:14
P.S. Кажется мне, в офтопике можно было задавать произвольный бодрейт, так что проблема не аппаратная.

Не совсем так. baud_base маздай менять тоже не может. Тут ведь вся соль в чём: драйвер должен посчитать этот дивизор (делитель) и записать его в регистры микросхемы 16550A, аналогично тому, что Вы делаете для МК. Например, для Atmel AVR это регистры UBRR [H,L]. Здесь такая же пестня. Чтобы получить скорость 115200, надо записать делитель 1, 57600 - 2, etc. Просто в маздае скорость задаётся по-другому: каждый раз она делает 115200/bps=custom_divisor и т.д.
Для того, чтобы менять скорость так точно, нужна другая микросхема UART. Есть микросхемы с более высокими частотами тактирования, хз как называются.
Вывод: "Используй то, что под рукою, и не ищи себе другое!" © :-(
Я, например, взял себе привычку программировать контроллеры только на стандартные скорости обмена. Не надо искушать судьбу и искать геморроя на свою задницу. Если не хватает скорости обмена, то надо искать другой интерфейс, подумать о USB, CAN и т.д.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: Произвольная скорость USART

Сообщение NickLion »

Понимаю, что ненадежный источник - http://en.wikipedia.org/wiki/Universal_asy...mitter#Baudrate
Но там в списке есть 16МГц. При делителе 64 как раз получаем то, что вроде надо. Но почему-то в списке стандартных частот этого значения нет, хотя 500КГц присутствет...
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: Произвольная скорость USART

Сообщение eddy »

NickLion писал(а):
02.12.2009 05:44
Понимаю, что ненадежный источник - http://en.wikipedia.org/wiki/Universal_asy...mitter#Baudrate

У меня UART - 16550A:

Код: Выделить всё

setserial -a /dev/ttyS0
/dev/ttyS0, Line 0, UART: 16550A, Port: 0x03f8, IRQ: 4
    Baud_base: 115200, close_delay: 50, divisor: 0
    closing_wait: 3000
    Flags: spd_normal skip_test

С той же википедии
16-byte buffers, TL=1,4,8,14; 115.2 kbps standard, many support 230.4 or 460.8 kbps. DMA-mode.

Интересно, как узнать, не относится ли мой USART к тем "many" :)

Вообще, я пытался установить скорость USART'а 115200бод, а на контроллере, соответственно, 125000бод. К сожалению, с такой погрешностью соотношения скоростей интерфейсы не взаимодействуют.
Пришлось остановиться на 19200, а данные накапливать в 36-байтные буферы (памяти у контроллера маловато) и высылать по окончании передачи пакета.
Сегодня попробую... Может, заработает :)
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
mrcashe
Сообщения: 18

Re: Произвольная скорость USART

Сообщение mrcashe »

eddy писал(а):
02.12.2009 09:12
Интересно, как узнать, не относится ли мой USART к тем "many" :)

Стандартные UART, расположенные на материнской плате, могут работать только со скоростями 115200/custom_divisor. Другое дело, специальные платы на основе, например, TL16C954 (есть такой чип). Так что, если случайно кто-то не подогнал Вам плату с таким чипом, не стоит лелеять надежду:-) Кстати, ядро Linux поддерживает такие чипы. Тем более, setserial обеспечивает такую гибкость, которая маздаю не снилась...
Мне удаётся гонять связь на 57600, МК AT89S8252, Fosc=24000000 Hz.
Надо понять, что возможности интерфейса RS-232 весьма ограничены и не стоит от него ждать чудес.
Вдогонку: отдельные платы последовательного интерфейса, установленные в разъёмы PCI, имеют адреса никак не равные 0x03f8, 0x03e8 etc. У меня, например, это 0x9b00 и т.д., в зависимости от расположения плат в слотах. К ней идёт диск с драйверами для маздая и текстовым файлом с пояснениями, как сконфигурировать его в Linux при помощи setserial в том случае, если ядро не подхватит его само. Например, ядро 2.6.17 неправильно эту плату хэндлило и приходилось править уставки при помощи setserial. Но 2.6.24 уже нормально схватывает.
Спасибо сказали:
mrcashe
Сообщения: 18

Re: Произвольная скорость USART

Сообщение mrcashe »

eddy писал(а):
02.12.2009 09:12
Вообще, я пытался установить скорость USART'а 115200бод, а на контроллере, соответственно, 125000бод. К сожалению, с такой погрешностью соотношения скоростей интерфейсы не взаимодействуют.

Так и есть, проходил я это дело и именно на этих скоростях: 115200 и 125000. Не берёт. И AVR не берёт, и MCS-51 тоже. Нечего и пытаться. Вообще, RS-232 хорош на скоростях типа 9600 и 19200. Есть варианты 28800 и 33600, но это мало что даёт. Для большего драйва надо RS-485 и CAN поверх него.
Спасибо сказали: