--- si-utf8.txt 2009-07-03 00:45:34.000000000 +0400
+++ si-utf8-new.txt 2009-07-03 01:12:03.000000000 +0400
@@ -10,7 +10,7 @@
Вообще, классическим считается создание make файла в котором бы были описаны все зависимости. И это, наверное, правильно. Но мне, выросшему на полностью интегрированных IDE вроде uVision или AVR Studio этот подход является глубоко чуждым. Поэтому буду делать по своему, все средствами студии.
Тыкай в кнопку с шестеренкой.
-Это настройки твоего проекта, а точнее настройки автоматической генерации make файла. На первой странице надо всего лишь вписать частоту на которой будет работать твой МК. Это зависит от фьюз битов, так что считаем что частота у нас 80000000Гц.
+Это настройки твоего проекта, а точнее настройки автоматической генерации make файла. На первой странице надо всего лишь вписать частоту на которой будет работать твой МК. Это зависит от фьюз битов, так что считаем что частота у нас 8000000Гц.
Следующим шагом будет настройка путей. Первым делом добавь туда директорию твоего проекта -- будешь туда подкладывать сторонние библиотеки. Make файл сгенерирован, его ты можешь поглядеть в папке default в своем проекте, просто пробегись глазами, посмотри что там есть.
@@ -43,7 +43,7 @@
int main(void)
{
-return 0;
+ return 0;
}
Все, первая простейшая программа написана, не беда что она ничего не делает. Разберем что же мы сделали.
@@ -53,9 +53,9 @@
int main(void)
{
-unsigned char i;
+ unsigned char i;
-return 0;
+ return 0;
}
unsigned значит беззнаковый. Дело в том, что в двоичном представлении у нас старший бит отводится под знак, а значит в один байт (char) влазит число не более +/-127, но если знак отбросить то влезет уже от 0 до 255. Обычно знак не нужен. Так что unsigned. i - это всего лишь имя переменной. Не более того.
@@ -64,21 +64,24 @@
Делаем так:
+#include <avr/io.h>
+
+#define BAUDRATE 9600L
+#define BAUDDIVIDER (F_CPU/(16 * BAUDRATE) - 1)
+#define HI(x) ((x) >> 8)
+#define LO(x) ((x) & 0xFF)
+
int main(void)
{
-unsigned char i;
+ unsigned char i;
-#define XTAL 8000000L
-#define baudrate 9600L
-#define bauddivider (XTAL/(16*baudrate)-1)
-#define HI(x) ((x)>>8)
-#define LO(x) ((x)& 0xFF)
-
-UBRRL = LO(bauddivider);
-UBRRH = HI(bauddivider);
-UCSRA = 0;
-UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE;
-UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
+ UBRRL = LO(BAUDDIVIDER);
+ UBRRH = HI(BAUDDIVIDER);
+ UCSRA = 0;
+ UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(0<<TXCIE);
+ UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
+
+ return 0;
}
Страшна? На самом деле кода тут всего пять последних строк. Все что #define это опции препроцессора. Они облегчат твои рутинные операции по вычислении нужных коэффициентов. В первой строке мы говорим что вместо XTAL можно смело подставлять 8000000, а L- указание типа, мол long - это тактовая частота процессора. То же самое baudrate - частота передачи данных по UART.
@@ -202,6 +205,9 @@
i++;
}
+// рекомендую использовать, хотя это вопрос стиля.
+for (;;) { ; }
+
Просто и наглядно. while вначале проверяет условие в скобках, если оно не 0 то выполняет кодовый блок, потом возвращается обратно и проверяет снова. Ну а раз там по дефолту 1, то цикл будет бесконечным. А i++ это всего лишь увеличение i на единицу. Должны же мы в цикле что нибудь сделать. Не пустым же оставлять, да и переменная у нас была сделана по приколу. Пускай тикает, а мы пока заглянем что появилось в дизассемблере.
Открываешь ты листинг, думаешь найти там новые ништяки... А ништяков то и нету!(с)
@@ -235,11 +241,11 @@
А наш цикл добавим команды установки и сброса бита в порту. Именно им будет определятся уровень напряжения на выходе.
while(1)
- {
- i++;
- LED_PORT=0<<LED1;
- LED_PORT=1<<LED1;
- }
+{
+ i++; // wtf?
+ LED_PORT |= _BV(LED1);
+ LED_PORT &= ~_BV(LED1);
+}
Во как! Если будешь гонять в отладчике, то увидишь как в I/O View будет менятся бит PORTB.0 Да, я тут не буду приводить, а ты обязательно загляни также в асм. Увидишь как там хитро компилятор выкрутился - он запомнил еще с инициализации что в R25 у нас 1 и заносит сразу оттуда в порт, не загружая ее повторно. А ноль берет из R1 - R1 у нас по дефолту 0 всегда, это не закон, но прихоть компилятора.
@@ -263,7 +269,8 @@
c:/winavr-20090313/lib/gcc/../../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"
это посереьзней. Компилятор говорит нам, что переменная препроцессора F_CPU не определена. Длительность задержки зависит от частоты процессора, а частоту то мы не указали. Не вопрос, добавляй куда нибудь в начало, но обязательно ПЕРЕД #include <util/delay.h>, иначе тебе еще три варнига будет - компилятор читает программу сверху вниз, а именно в delay.h ему потребовалась эта переменная. У меня вот так:
-#define F_CPU 8000000L
+// неправильное решение!!! должно быть определено в мейке для gcc ключь -DF_CPU=8000000UL,
+// создаваемые студией мейки емнип прописывают.
#include <avr/io.h>
#include <util/delay.h>
@@ -283,13 +290,14 @@
Отрывай от нее все причиндалы, они нужны только на этапе описания, и вставляй в код, не забыв указать задержку. Вот так вот:
while(1)
- {
- i++;
- LED_PORT=0<<LED1;
- _delay_ms(1000);
- LED_PORT=1<<LED1;
- _delay_ms(1000);
- }
+{
+ i++;
+ LED_PORT = 0<<LED1;
+ _delay_ms(1000);
+ LED_PORT = 1<<LED1;
+ _delay_ms(1000);
+}
+
Погасили, подождали секунду, зажгли, подождали секунду, перешли в начало цикла.
Ок, надо бы проверить вообще то у нас получилось или не то? Задержка точно выдерживается? Сейчас я тебя научу пользоваться брейпоинтами. Программу можно выполнять не только пошагово, но и запустить ее на исполнение, а остановку чтобы поглядеть что происходит делать только по специальным остановкам - брякам. Выделяй строку где у нас включается диод и жми F9 - будет жирная красная точка. Тут прога встанет колом. То же самое сделай на строке где прога гасит диод. Запускай программу на компиляцию-эмуляцию и жми не F11 как обычно, а F5
@@ -319,6 +327,7 @@
#include <avr/interrupt.h>
Оформляется прерывание в WinAVR как
+
ISR(вектор прерывания)
{
}
@@ -352,76 +361,90 @@
Для определения байта можно применить конструкцию Switch-case
В итоге, получилась такая вещь:
+
ISR(USART_RXC_vect)
{
-switch(UDR)
+ switch(UDR)
{
- case '1': LED_PORT = 1<<LED2; break;
- case '0': LED_PORT = 0<<LED2; break;
- default: break;
+ case '1':
+ LED_PORT = 1<<LED2;
+ break;
+
+ case '0':
+ LED_PORT = 0<<LED2;
+ break;
+
+ default:
+ break;
}
}
+
Как видишь, у нас в свитче берется UDR и в зависимости от того чему он равен, символу нуля или символу единицы осуществляется переход. Если не то и не другое, то переход на default и сразу выход из свитча - break. break это вообще выход из программной структуры. Например, если сделаешь break в цикле, то выпадешь на следующую итерацию цикла.
В значении case можно было бы написать и код символа, было бы case 0x31: но, как я говорил, никаких цифр. Если есть возможность их избежать - избегай всеми силами. Пусть за тебя препроцессор компилятора отдувается. В частности 'символ' это ASCII код символа. Удобно!
Да, обрати внимание на то, что в каждой строке case стоит break. Это неспроста! Если break там не будет, то пройдя на case 0 и выполнив там все процессор перейдет к следующему case и так далее, пока не выйдет из него совсем или не нарвется на break.
Что зажигания диодов, то тут, как видишь, пришлось добавить еще и LED2, прописав его в дефайнах. И не забыв проинициализировать его порт на вход! В итоге, стало выглядеть так:
- LED_DDR = 1<<LED1|1<<LED2;
+ LED_DDR = (1<<LED1)|(1<<LED2);
Правда сразу ничего у меня не заработало -- посыпалось куча ошибок. Пришлось взять все дефайны в кучу и поднять их над всем кодом -- компилятор же читает код сверху вниз так что вначале он должен прочитать все макроопределения прежде чем ему встретятся операции с ними.
Вот текущий код целиком:
-#define F_CPU 8000000L
+
+#include <avr/io.h>
+#include <util/delay.h>
+
+// повторять не буду
+#define BAUDRATE 9600L
+#define BAUDDIVIDER (F_CPU/(16 * BAUDRATE) - 1)
+#define HI(x) ((x) >> 8)
+#define LO(x) ((x) & 0xFF)
+
#define LED1 0
#define LED2 1
#define LED_PORT PORTB
#define LED_DDR DDRB
-
-#include <avr/io.h>
-#include <util/delay.h>
-
ISR(USART_RXC_vect)
{
-switch(UDR)
+ switch(UDR)
{
- case '1': LED_PORT = 1<<LED2; break;
- case '0': LED_PORT = 0<<LED2; break;
- default: break;
+ case '1':
+ LED_PORT = 1<<LED2;
+ break;
+
+ case '0':
+ LED_PORT = 0<<LED2;
+ break;
+
+ default:
+ break;
}
}
int main(void)
{
-volatile unsigned char i;
-
-#define XTAL 8000000L
-#define baudrate 9600L
-#define bauddivider (XTAL/(16*baudrate)-1)
-#define HI(x) ((x)>>8)
-#define LO(x) ((x)& 0xFF)
-
-UBRRL = LO(bauddivider);
-UBRRH = HI(bauddivider);
-UCSRA = 0;
-UCSRB = 1<<RXEN|1<<TXEN|1<<RXCIE|0<<TXCIE;
-UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
+ unsigned char i;
+ // USART init
+ UBRRL = LO(BAUDDIVIDER);
+ UBRRH = HI(BAUDDIVIDER);
+ UCSRA = 0;
+ UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE)|(0<<TXCIE);
+ UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
-LED_DDR = 1<<LED1|1<<LED2;
+ LED_DDR = 1<<LED1|1<<LED2;
+ while(1)
+ {
+ i++;
+ LED_PORT = 0<<LED1;
+ _delay_ms(1000);
+ LED_PORT = 1<<LED1;
+ _delay_ms(1000);
+ }
-while(1)
- {
- i++;
- LED_PORT=0<<LED1;
- _delay_ms(1000);
- LED_PORT=1<<LED1;
- _delay_ms(1000);
- }
-
-return 0;
+ return 0;
}
Вроде все написано. Запускай код на эмуляцию. Но вот как проверить прерывание? В данном случае, в студии, для этого нет никаких эмуляторов терминала (вообще есть hapsim, а еще можно отлаживать в Proteus или VMLAB, но об этом я расскажу попозже). Но можно сделать вид, что пришло прерывание, словно терминал сработал. Процессор определяет, что произошло прерывание когда периферия, его генерирующая, поднимет флаг прерывания. У USART на прием флаг RxC вот возьми и ткни его, мол прерывание свершилось. Вручную выстави этот бит, найдя его в окне i/o view в ветви USART. Поставил, а потом сделай пару шагов по F11 и... нифига не произошло! А почему? а потому, что мы забыли эти прерывания врубить. Частая ошибка начинающих. Дело в том, что мало разрешить прерывания при инициализации UART -- это мы разрешили локальные прерывания, уартовские, а есть еще флаг глобального разрешения - флаг I он по дефолту сброшен.