реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Использование USART в качестве SPI.
Finik33
сообщение Oct 25 2011, 14:25
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 16
Регистрация: 4-04-11
Пользователь №: 64 123



Доброго времени суток, товарищи.

Для дома для семьи задумал я сваять двухканальный дешифратор энкодеров с семисегментной индикацией. Достал контроллер atmega8 и стал кодить его на С... Практически все закодил, только одно "но" повлекло за собой трудности, которые с разбегу решить не удалось. По порядку.

Поскольку портов в данном контроллере мало, а должен он сканировать 2 энкодера, выдавать 3-битный код на 4 аналоговых мультиплексора и показывать текущее значение режима на 2 4х-разрядных семисегментных индикатора, было принято решение значения индикации сохранять в 2 8-разрядных последовательных регистра 74хх164 и стробировать. Получившаяся функция записи данных в регистр выглядит так:
Код
static inline void WriteLedValue(uint8_t value)
{

    for (uint8_t i = 0; i < 8; i++)    //    толкаем 8 раз одну цифру
    {
        CBI(STR);        //    строб в 0
        if (value & 0x1)
        {
            SBI(LED);    //    если последняя 1, устанавливаем бит
        }
        else
        {
            CBI(LED);    //    иначе стираем
        }
        SBI(STR);        //    формируем фронт строба для записи в регистр
        value >>= 1;    //    первый разряд протолкнули, сдвигаем
    }
}

Однако одна итерация составляет 14 тактов, что в сумме дает 14*16 = 224 такта. При условии, что тактовая частота процессора планируется 1МГц, а частота опроса энкодеров и она же частота стробирования - 1кГц, получается, что добрая 1/3 цикла свечения тратится тупо на проталкивание бит...

Я было подумал, что при написании кода на асме получилось бы максимум 3 такта на цикл (сдвиг, проверка регистра переноса, запись бита в порт), но умные люди посоветовали для этой задачи использовать готовое решение - SPI. Посмотрев расположение пинов, я пришел к выводу, что использовать классический SPI мне будет неудобно, а вот если перейти на Atmega88, то можно задействовать USART в режиме SPI. И вот тут начались непонятные казусы...

До этого я весь код писал в AVR Studio 5 + Proteus, проблем не было. При попытке же симулировать данный порт в режиме SPI получается странная штуковина: первый вызов функции в режиме отладки в студии проскакивает влет, без задержки на проверку опустошения буфера, срабатывает только при повторном. А в Протеусе последняя запись выдается всегда ПЕРВОЙ
Код
static inline void WriteLedValue(uint8_t value)
{
    while (!(UCSR0A & (1<<UDRE0)));
    UDR0 = value;
    while (!(UCSR0A & (1<<UDRE0)));
}

Вызов выглядит так
Код
    SBI(_CS);        //    гасим индикаторы
    DIG(digit);        //    выставляем адрес разряда индикации
    CBI(_RS);        //    сбрасываем регистры
    SBI(_RS);        //    надеюсь, достаточно 1 такта
    WriteLedValue(Symbols[0]);    //    записываем значения в регистр
    WriteLedValue(Symbols[1]);
    CBI(_CS);        //    зажигаем индикаторы

При таком раскладе у меня сначала проталкивается пустой символ, затем "2". В результате на индикаторах горит 2 в первом разряде одного канала и ничего - во втором. При следующей итерации во втором разряде первого канала горит 0, как и положено, а во втором канале горит "2", которая должна была гореть на предыдущей итерации... Методом научного тыка нашел решение:
Код
    WriteLedValue(Symbols[0]);    //    записываем значения в регистр
    WriteLedValue(Symbols[1]);
    WriteLedValue(0);            //    хрень какая-то, но по-другому не симулируется

При таком раскладе первым проталкивается 0 (!!!), хотя вызов идет третьим, затем первый канал и второй в нужном порядке. Но так получается лишние 16+ тактов на пропихивание "пустышки", что не есть тру...
Конфигурация порта такая:
Код
    //    Init ports as outputs
    DDRB |= 0x3F;
    DDRC |= 0x3F;
    DDRD |= 0x3F;

    //    Init timer
    TIMSK0 |= 1<<TOIE0;                    //    timer int enable
    TCCR0B |= 1<<CS01 | 0<<CS00;        //    start timer, divider 64
                                        //    int each 1ms
    //    Init USART
    UBRR0    = 0;                        //    максимальная скорость Fclk/2
    UCSR0C    = 1<<UMSEL01 | 1<<UMSEL00 | 1<<UCPHA0 | 1<<UCPOL0 | 0<<UDORD0;    //    конфигурируем USART в режим SPI mode 0
    UCSR0B    = 1<<TXEN0;                //    включение пина передатчика, приемник при этом используется как порт В/В
    UBRR0    = 0;

Помогите, пожалуйста, я уже весь мозг сломал...

Для большей понятности выкладываю два примера со схемками в Протеусе.
encoders_atm8_v5 - это как должно работать идейно, но с неоптимальным временем записи
encoders_atm88_v5 - это то, что я пытаюсь сделать с помощью SPI. Сейчас там используется 3 вызова функции, в чем можно убедиться, активировав осцилл и прерывание на ноге 1 U3:A
Прикрепленные файлы
Прикрепленный файл  _Projects.zip ( 341.1 килобайт ) Кол-во скачиваний: 12
 
Go to the top of the page
 
+Quote Post
Палыч
сообщение Oct 25 2011, 20:24
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Не нашёл "криминала" в Ваших программах...
Однако, некоторые Ваши решения - удивляют. Вы используете USART в режиме SPI в то время, как в МК присутствует "настоящий" SPI. Да, и программный SPI очень даже не плохой - Вы неверно оценили "затраты" на его реализацию: нет необходимости выводить новое значение на индикатор, ести оно (значение) не меняется. Кроме того, если значение и меняется, то нет никакой необходимисти менять его с частотой 1000 раз в секунду. Вероятно, будет достаточна частота 5...10 раз в секунду. Поэтому даже при программном SPI "затраты" составят не 1/3, а менее 0.3%.
Go to the top of the page
 
+Quote Post
Finik33
сообщение Oct 26 2011, 14:30
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 16
Регистрация: 4-04-11
Пользователь №: 64 123



Цитата(Палыч @ Oct 26 2011, 00:24) *
Однако, некоторые Ваши решения - удивляют. Вы используете USART в режиме SPI в то время, как в МК присутствует "настоящий" SPI.

Я же написал, мне не удобно использовать штатный из-за расположения пинов на корпусе. Но самое главное - задействование "настоящего" SPI влечет за собой активацию ножек MISO и SS, а мне свободных ножек катастрофически не хватает. Вся эта катавасия - именно по причине нехватки.
Цитата(Палыч @ Oct 26 2011, 00:24) *
Да, и программный SPI очень даже не плохой - Вы неверно оценили "затраты" на его реализацию: нет необходимости выводить новое значение на индикатор, ести оно (значение) не меняется. Кроме того, если значение и меняется, то нет никакой необходимисти менять его с частотой 1000 раз в секунду. Вероятно, будет достаточна частота 5...10 раз в секунду. Поэтому даже при программном SPI "затраты" составят не 1/3, а менее 0.3%.

К сожалению, в одну единицу времени может гореть только 1 разряд из четырех, опять же по причине нехватки ножек портов. Поэтому приходится стробировать разряды с частотой, не заметной для глаз (от 100Гц и выше), использовать индикаторы с общим анодом (катодом) и динамически менять значение регистра. Поскольку разрядов 4, то каждый разряд горит со скважностью 1/4. Это максимальная длительность при условии, что время записи в регистр и расчет нового значения для следующего разряда - мгновенные. Но это невозможно, поскольку на расчет и выставление тратятся такты... Поэтому реальная скважность меньше. При 1/8 цифры уже достаточно тускло горят, что не есть хорошо.

Хотя доля рационализма в Ваших словах есть: я действительно каждое прерывание вычисляю значение константы для запихивания в буфер. Гораздо правильнее посчитать 1 раз и пересчитывать при вращении энкодеров.

Что касается сабжа, то я на свежую голову разобрался. Оказывается, регистр UDRE0 показывает состояние буфера регистра вывода. Т.е. если в текущий момент идет передача, то поступившее значение кладется в этот буфер и флаг обнуляется. Очевидно это не было написано, но если сопоставить с возможностью передавать пакеты по 16 бит, все стыкуется: первая запись в регистр UBRR0 инициализирует передачу и помещается непосредственно в сдвиговый регистр, при этом флаг UDRE0 = 1. Последующая мгновенная запись в этот регистр помещается в буфер, а флаг UDRE0 сбрасывается в 0. Он снова становится 1 при опустошении регистра передачи и записи значения из буфера на передачу вновь. Т.о. данный флаг никак не подходит для передачи 8-битных данных.

Поэтому я решил использовать флаг TXC0
Код
static inline void WriteLedValue(uint8_t value)
{
    SBI(UCSR0B,TXEN0);        //    В режиме симуляции в Протеусе идет постоянная генерация байта, поэтому приходится отключать и включать порт
    UDR0 = value;
    while (!(UCSR0A & 1<<TXC0));
    SBI(UCSR0A, TXC0);        //    какой-то бред, бит стирается, если его записывать!
    CBI(UCSR0B,TXEN0);
}

Однако с ним тоже возникли грабли, которые весьма не очевидны: чтобы его сбросить, в него надо писать 1, а не 0. Также в Протеусе работают неадекватно режимы конфигурации фазы и полярности: не соответствуют даташиту, в режиме 0-2 идет постоянная циклическая передача последнего байта, из-за чего приходится принудительно выключать передатчик. С режимом 3 все нормально, поэтому, скорее всего, это глюк симулятора
Go to the top of the page
 
+Quote Post
Палыч
сообщение Oct 26 2011, 18:16
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(Finik33 @ Oct 26 2011, 18:30) *
Что касается сабжа, то я на свежую голову разобрался.
Не понял - в чем Вы разобрались? Такое поведение флага UDRE описано в DS (и во всех книжках на русском). Оно (поведение этого флага) - вполне логичное и никак не мешает передаче восьмибитных данных...

Цитата(Finik33 @ Oct 26 2011, 18:30) *
Однако с ним тоже возникли грабли, которые весьма не очевидны: чтобы его сбросить, в него надо писать 1, а не 0.
Флаги в AVR (за очень редким исключением) сбрасываются записью единицы.
Go to the top of the page
 
+Quote Post
Finik33
сообщение Oct 27 2011, 14:06
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 16
Регистрация: 4-04-11
Пользователь №: 64 123



Цитата(Палыч @ Oct 26 2011, 22:16) *
Не понял - в чем Вы разобрались?

Да я, собсно, полчаса потратил выше, чтобы объяснить это... sm.gif Но не важно уже, программа отлажена, приступаю к разводке)
Go to the top of the page
 
+Quote Post
codex
сообщение Jan 17 2012, 08:31
Сообщение #6





Группа: Участник
Сообщений: 13
Регистрация: 11-09-08
Пользователь №: 40 138



и как - всё работает?

поможете переделать работу с SD-картой с SPI на USART?
Go to the top of the page
 
+Quote Post
genium
сообщение Jan 28 2012, 19:12
Сообщение #7





Группа: Новичок
Сообщений: 1
Регистрация: 28-01-12
Пользователь №: 69 937



Finik Здрасти!

Столкнулся с теми же граблями в Proteuse!!! на чипе все нормально работает ил как?

Жду ответа плата у мну получается не дешевая не хотелось бы попасть в просак. maniac.gif
Go to the top of the page
 
+Quote Post
Finik33
сообщение Mar 7 2012, 09:33
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 16
Регистрация: 4-04-11
Пользователь №: 64 123



На железке я пока так и не попробовал, потому что сменил работу и времени на собственные поделки совсем не остается sad.gif
Go to the top of the page
 
+Quote Post
Finik33
сообщение Apr 20 2012, 13:16
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 16
Регистрация: 4-04-11
Пользователь №: 64 123



Цитата(genium @ Jan 28 2012, 23:12) *
Finik Здрасти!
Столкнулся с теми же граблями в Proteuse!!! на чипе все нормально работает ил как?

Наконец зашил в железо. Работает почти как надо. Единственное, пришлось выставить биты полярности и фазы в 0 (для регистра HC164) и биты перевернуть: в контроллере ФИФО, а в Протеус почему-то напрямик валит
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st July 2025 - 21:36
Рейтинг@Mail.ru


Страница сгенерированна за 0.01455 секунд с 7
ELECTRONIX ©2004-2016