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

 
 
> Использование 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



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

 


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


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