В этой статье я постораюсь собрать те грабли, на которые я сама нарвалась, пока учила Си. Не повторяйте моих ошибок!
Берем строку из Flash.
Эта программа должна выводить в порт приветствие. На самом деле она выводит бред.
#include <avr/io.h>
#include <avr/pgmspace.h>
prog_char hello_str[]=«Hello AVR!»;
void puts(char * str)
{
while(*str!=0)
{
PORTB=*str++;
}
}
void main(void)
{
puts(Hello_str);
}
AVR-GCC понятия не имеет, куда показывает указатель — на память программ или на память данных. По умолчанию он честно берет данные из ОЗУ. Чтобы взять cтроку из Flash-памяти, пользуйтесь макросами из файла «pgmspace.h». Например так:
#include <avr/io.h>
#include <avr/pgmspace.h>
prog_char hello_str[]=«Hello AVR!»;
void puts(char * str)
{
while(PRG_RDB(str) != 0)
{
PORTB=PRG_RDB(str++);
}
}
void main(void)
{
puts(Hello_str);
}
Ждем битик в порту.
Эта функция должна ждать, пока битик в порту ввода-вывода станет ноликом… На самом деле программа здесь виснет.
void Wait_for_bit()
{
while( PINB & 0x01 );
}
При включеной оптимизации компилятор сначала вычисляет (PINB & 0x01) и кладет значение в регистр, а потом проверяет этот регистр. Для компилятора неочевидно, что значение PINB может измениться само по себе, а не в результате действий программы.
Решение: Делайте проверку внутри цикла или пользуйтесь макросами из файла «sfr_defs.h» (включен в «io.h»). Например так:
void Wait_for_bit()
{
while ( bit_is_set(PINB,0) );
}
Ждем флага от прерывания.
Эта функция должна ждать, пока не случится прерывание… На самом деле программа здесь виснет.
unsigned char flag;
void Wait_for_interrupt()
{
while(flag==0);
flag=0;
}
SIGNAL(SIG_OVERFLOW0)
{
flag=1;
}
Причина точно такая же. Для компилятора неочевидно, что значение переменной flag может измениться само по себе.
Решение: Объявить переменную flag с модификатором «volatile».
volatile unsigned char flag;
void Wait_for_interrupt()
{
while(flag==0);
flag=0;
}
SIGNAL(SIG_OVERFLOW0)
{
flag=1;
}
Организуем задержку
Эта функция должна просто занять время… На самом деле она выполняется очень быстро.
void Big_Delay()
{
long i;
for(i=0;i<1000000;i++);
}
Причина в слишком умной оптимизации AVR-GCC. Для компилятора очевидно, что функция ничего не вычисляет — она ничего не возвращает и не изменяет глобальных и статических переменных. Такую функцию можно «заоптимизировать» до нуля. (Но компилятор не так жесток — он оставляет несколько итераций цикла для целей отладки).
Решение: Пользуйтесь макросами из файла «delay.h» или впишите в тело цикла что-нибудь на ассемблере, чтобы сбить компилятор с толку.
#define nop() {asm(«nop»);}
void Big_Delay()
{
long i;
for(i=0;i<1000000;i++) nop();
}
Продолжение следует...
Удачи! =))
5 комментариев
может ссылку или книжку подскажеш?????