Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: WinAVR: оптимизация
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Arlleex
Добрый день. Столкнулся с проблемой оптимизации программы для микроконтроллера ATTiny2313. В Makefile выбрал оптимизацию "s", что означает "по размеру".
Вот эту функцию он выполняет не верно. Возвращаемое значение функции (байт) сдвинуто на 1 влево. В принципе есть определенные догадки, как рассуждает оптимизатор при оптимизации, но по другому нельзя, интерфейс требует установить линию данных в Hi-Z состояние, т.е. судя по логике оптимизатора можно просто спалить линию микроконтроллера и/или ведомой микросхемы (в моем случае это RTC DS1305 3-Wire). Но! При установке оптимизации уровня 1 все работает как задумано (в чем, собственно, я и не сомневался).
Функцию привожу:
Цитата
uint8_t rtc_read_byte(uint8_t address)
{
uint8_t i; // промежуточный счетчик бит
uint8_t buffer; // промежуточный буффер данных
uint8_t rtc_data; // данные
rtc_data=0x0; // запись данных
PORTD=PORTD | (1<<CS); // выбор периферийного устройства
for(i=0; i<8; ++i)
{
buffer=address; // копирование данных
buffer=buffer & 0x1; // выделение младшего бита
if (buffer==0x1)
{
PORTD=PORTD | (1<<DATA); // установка DATA
}
else
{
PORTD=PORTD & (~(1<<DATA)); // сброс DATA
}
PORTD=PORTD | (1<<CLK); // установка CLK
if (i<7)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
}
address=address>>1; // сдвиг вправо
}
PORTD=PORTD & (~(1<<DATA)); // настройка подтягивающего резистора линии DATA порта D
DDRD=DDRD & (~(1<<DATA)); // настройка линии DATA порта D
for(i=0; i<8; ++i)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
buffer=PIND & (1<<DATA); // чтение данных
if (buffer!=0)
{
rtc_data=rtc_data | (1<<i); // установка бита
}
PORTD=PORTD | (1<<CLK); // установка CLK
}
PORTD=PORTD & (~(1<<CS)); // сброс CS
DDRD=DDRD | (1<<DATA); // настройка линии DATA порта D
PORTD=PORTD & (~((1<<DATA) | (1<<CLK) | (1<<CS))); // сброс DATA, CLK, CS
return rtc_data; // выход из функции
}
demiurg_spb
Оптимизатор тут нипричём. Он никак не может изменить очерёдность обращения к волатильным переменным, каковыми являются и PORTx.
Что приходит на ум это отсутствие каких либо таймингов в вашем коде.
Я уверен что часы просто видимо не успевают т.к. вы не соблюдаете
3-WIRE AC ELECTRICAL CHARACTERISTICS (см. datasheet).
Посмотрите как это пишется...
Код
uint8_t spi_transfer(uint8_t data)
{
    for (int i=0; i<8; i++)
    {
        SPI_SCK(0);
        SPI_MOSI(data & (1<<7));        // write data at the begin of cycle
        spi_delay();

        SPI_SCK(1);
        spi_delay();
        data = (data<<1) | SPI_MISO();  // read data at the end of cycle
    }

    return data;    
}
Палыч
Уж не знаю: как у Вас получается принимать правильно при другом уровне оптимизации...
Насколько я понял: у Вас CPOL=0. При этом DS1305 выставит очередной бит для считывания его МК при установки высокого уровня на CLK. Вы же его считываете при низком уровне на CLK, т.е. "забегаете вперед" на один бит. Вот результат и получается сдвинутым...
Arlleex
Цитата(Палыч @ Feb 6 2012, 15:52) *
Уж не знаю: как у Вас получается принимать правильно при другом уровне оптимизации...
Насколько я понял: у Вас CPOL=0. При этом DS1305 выставит очередной бит для считывания его МК при установки высокого уровня на CLK. Вы же его считываете при низком уровне на CLK, т.е. "забегаете вперед" на один бит. Вот результат и получается сдвинутым...

У меня 3-Wire подключение.
Цитата
PORTD=PORTD | (1<<CS); // выбор периферийного устройства
for(i=0; i<8; ++i)
{
buffer=address; // копирование данных
buffer=buffer & 0x1; // выделение младшего бита
if (buffer==0x1)
{
PORTD=PORTD | (1<<DATA); // установка DATA
}
else
{
PORTD=PORTD & (~(1<<DATA)); // сброс DATA
}
PORTD=PORTD | (1<<CLK); // установка CLK
if (i<7)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
}
address=address>>1; // сдвиг вправо
}

Вот это выполняется правильно, адрес записывается.
Строки PORTD=PORTD | (1<<CLK); // установка CLK
if (i<7)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
}
указывают, что как только мы передали 8-й бит слова адреса, мы линию CLK не сбрасываем в 0, а сбрасываем ее тут:
Цитата
for(i=0; i<8; ++i)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
buffer=PIND & (1<<DATA); // чтение данных
if (buffer!=0)
{
rtc_data=rtc_data | (1<<i); // установка бита
}
PORTD=PORTD | (1<<CLK); // установка CLK
}

, поскольку при заднем фронте CLK произойдет вывод данных из RTC. Если бы мы это сделали раньше, чем написали бы строки
Цитата
PORTD=PORTD & (~(1<<DATA)); // настройка подтягивающего резистора линии DATA порта D
DDRD=DDRD & (~(1<<DATA)); // настройка линии DATA порта D

RTC выставил бы данные, и МК не переопределя линию на вход, спалил бы и себе линию, и возможно RTC.
Насчет таймингов. У меня частота микроконтроллера остается стандартной и поэтому время выполнения даже самой короткой операции не превышает 1мкс.
Палыч
Цитата(Arlleex @ Feb 6 2012, 16:33) *
указывают, что как только мы передали 8-й бит слова адреса, мы линию CLK не сбрасываем в 0, а сбрасываем ее тут:
....
, поскольку при заднем фронте CLK произойдет вывод данных из RTC. Если бы мы это сделали раньше, чем написали бы строки
...
RTC выставил бы данные, и МК не переопределя линию на вход, спалил бы и себе линию, и возможно RTC.

Не-а...
Еще раз: вывод данных из RTC - по переднему фронту (из низкого в высокий). Т.е. линию CLK "опустить" после вывода последнего бита адреса (if с проверкой i<7 - убрать); настрока на ввод DATA; данные забирать при высоком уровне на CLK (высокий CLK - чтение DATA - низкий CLK), у Вас сейчас - наоборот.

PS. Посмотрите внимательно на рисунок 7 из DS
Arlleex
Цитата(Палыч @ Feb 6 2012, 17:03) *
Не-а...
Еще раз: вывод данных из RTC - по переднему фронту (из низкого в высокий). Т.е. линию CLK "опустить" после вывода последнего бита адреса (if с проверкой i<7 - убрать); настрока на ввод DATA; данные забирать при высоком уровне на CLK (высокий CLK - чтение DATA - низкий CLK), у Вас сейчас - наоборот.

PS. Посмотрите внимательно на рисунок 7 из DS

Посмотрите рисунок 9. У меня подключение не SPI, а 3-Wire.
такое ощущение, что в куске
Цитата
for(i=0; i<8; ++i)
{
PORTD=PORTD & (~(1<<CLK)); // сброс CLK
buffer=PIND & (1<<DATA); // чтение данных
if (buffer!=0)
{
rtc_data=rtc_data | (1<<i); // установка бита
}
PORTD=PORTD | (1<<CLK); // установка CLK
}

i изначально не устанавливается равным 0. А устанавливается равным 1... ну а дальше понятно почему сдвиг... вот только почему i=1 - не знаю..
HNK0
Цитата(Arlleex @ Feb 6 2012, 10:39) *
Посмотрите рисунок 9. У меня подключение не SPI, а 3-Wire.
такое ощущение, что в куске

i изначально не устанавливается равным 0. А устанавливается равным 1... ну а дальше понятно почему сдвиг... вот только почему i=1 - не знаю..


a вы так: for(i=0; i<8; i++) вместо for(i=0; i<8; ++i)
demiurg_spb
Цитата(HNK0 @ Feb 7 2012, 04:27) *
a вы так: for(i=0; i<8; i++) вместо for(i=0; i<8; ++i)

Читаем букварь по си...
Код
int main(void)
{
    for (int i=0; i<8; i++)
    {
        putchar('0'+i);
    }

    puts("");

    for (int i=0; i<8; ++i)
    {
        putchar('0'+i);
    }

    return (0);
}

В консоли:
Код
01234567
01234567


Цитата(Arlleex @ Feb 6 2012, 16:39) *
У меня подключение не SPI, а 3-Wire.
Расскажите нам нам в чём принципиальная разница?
Заодно о разнице между I2C и TWI и между бордюром и поребриком:-)

Цитата(Arlleex @ Feb 6 2012, 15:33) *
Насчет таймингов. У меня частота микроконтроллера остается стандартной и поэтому время выполнения даже самой короткой операции не превышает 1мкс.
Ну да и уровень оптимизации никак не сказывается на таймингах...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.