С двумя целочисленными типами переменных мы уже сталкивались. Теперь познакомимся с наиболее родным типом данных для нашего 8-ми битного AVR микроконтроллера – unsigned char
unsigned char – это беззнаковое символьное число, его размер 1 байт, а диапазон значений от 0 до 255.
unsigned char tmp; //объявление переменной
unsigned char var1, var2; //можно объявлять сразу несколько переменных
unsigned char led = 0; //объявляя переменную ей можно присвоить значение
Написание имени переменной подчиняется определенным правилам. Можно использовать заглавные и строчные буквы, цифры и символ подчеркивания _. Первый символ имени переменной должен быть либо буквой, либо символом подчеркивания. В качестве имени переменной нельзя использовать ключевые слова (while, if, return и все остальные зарезервированные слова). Желательно, чтобы имя переменной отражало смысл ее содержимого, это облегчает чтение программы.
У AVR микроконтроллерa есть несколько видов памяти – память программ или flash память, EEPROM, оперативная память (ОЗУ) и регистры. Память программ, помимо своего прямого назначения, используется для хранения констант – переменых которые не меняют своего значения в ходе выполнения программы. EEPROM имеет ограниченное число циклов перезаписи, поэтому хранить там переменную можно только если ее значение изменяется довольно редко. А вот оперативная память и регистры наилучшим образом подходят для хранения переменных.
Переменные объявленные внутри функции называются локальными, их область видимости ограничена телом функции, Это означает, во-первых, что другие функции не могут обращаться к этим переменным (они их не видят), а во вторых, что в функциях могут быть объявлены переменные с одинаковыми именами. Локальные переменные хранятся в регистрах общего назначения микроконтроллера. При выходе из функции значение локальной переменной не сохраняется. Если нам нужно сохранять значение локальной переменной, то нужно использовать статические локальные переменные. Они сохраняются в оперативной памяти. Об этом мы поговорим позже.
Переменные объявленные вне функций называются глобальными, к ним можно обратиться из любой функции. Глобальные переменные хранятся в оперативной памяти. Когда происходит обращение к глобальной переменной, ее значение считывается в регистры общего назначения.
Итак, вот кусок нашей программы
//программа бегущего светодиода
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
unsigned char led;
DDRC = 255;
while(1){
}
return 0;
}
С учетом новых знаний, можно написать уже это.
//программа бегущего светодиода
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
unsigned char led = 1;
DDRC = 255;
while(1){
PORTC = ~led;
__delay_cycles(400000);
led = led<<1;
}
return 0;
}
Выражение на Си читаются справа налево.
Строчка PORTC = ~led; означает – взять значение переменной led, проинвертировать его и присвоить PORTC (записать в PORTC). Значение led при этом никак не меняется!
Величину программной задержки я уменьшил, чтобы бегущий светодиод эффектней смотрелся.
Данный вариант программы будет работать, но ... не долго. Как только единичка в переменной led будет сдвинута 8 раз, она затрется и в led окажется нулевое значение. А ноль как не сдвигай, толку никакого. Получается, что нам нужно после каждого сдвига проверять, не обнулилась ли наша переменная и как только это произойдет снова записать в led единицу. Для этого воспользуемся оператором ветвления if
Операторы ветвления предназначены для управления ходом выполнения программы. Они выбирают, какую часть программы выполнять в зависимости от истинности или ложности заданных условий. Одним из типов ветвлений в Си является if ... else. Этот тип ветвления осуществляет выбор между двумя альтернативами. Он имеет следующий синтаксис.
if (condition){
statement1;
statement2;
}
else{
statement3;
statement4;
}
Если условие (condition) окажется истинным, будет выполняться блок, следующий сразу после if, если ложным – блок следующий после else. В качестве условия может выступать переменная, выражение или функция, возвращающая значение. Чаще всего условие задается операторами отношени. В языке Си есть шесть таких операторов.
Оператор | Значение |
== | равно |
!= | не равно |
> | больше |
>= | больше или равно |
< | меньше |
<= | меньше или рав |
Выражение условия, записанное с помощью операторов отношения это по сути своей обычное школьное неравенство, с тем лишь отличием, что используются немного другие знаки.
Вот как, например, записать для нашей программы проверку переменной led на нулевое значение:
if (led == 0){ //если переменная led равна 0
…… //выполнить этот блок
}
__________________________________________
Дополнительные сведения по оператору if…else
• Использование else не является обязательным.
if (condition){
statement1;
statement2;
}
• Если блок состоит из одного оператора, то фигурные скобки {} можно не ставить.
if (condition)
statement1;
• Ветвления могут быть вложенными.
if (condition1){
if (conditon2)
statement1;
}
else{
if (condition3) {
statement3;
statement4;
}
}
____________________________________________________
Наиболее типичные ошибки при использовании оператора if это:
• ставить точку с запятой после условия
if (tmp == 0);
tmp= 1; //tmp всегда будет присваиваться 1
Синтаксически здесь все правильно и компилятор не выдаст ошибку, однако программа будет вести себя не так, как вы задумали. Эта ошибка еще встречается при использовании цикла while
• при задании условия вместо оператора отношения равно == использовать оператор присвоения =
if (tmp = 0){ //в этом случае tmp присвоится 0, а блок операторов не будет выполнятся
tmp = 1;
……….
}
IARовский компилятор выдает предупреждение на подобную ошибку.
• неправильно организовывать вложенные ветвления
if (condition1)
if (conditon2)
statement1;
else
statement3;
По задумке программера else должен относиться к первому оператору if. А компилятор связывает else со вторым оператором if. Общее правило здесь такое - else связан с последним из операторов if, который не имеет собственного блока else. Чтобы этого не происходило, ограничивайте тело оператора if…else фигурными скобками. Вообще считается, что скобки нужно ставить всегда, даже если тело оператора if…else (да и многих других операторов, где допускается опускать скобки) состоит из одного выражения.
Вот так будет правильней.
if (condition1){
if (conditon2)
statement1;
}
else
statement3;
А так еще лучше, хотя я, признаюсь, экономлю на скобках и использую обычно предыдущий вариант.
if (condition1){
if (conditon2){
statement1;
}
}
else{
statement3;
}
Вот мы и подошли к окончательной версии нашей программы.
//программа бегущего светодиода
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
unsigned char led = 1;
DDRC = 255;
while(1){
PORTC = ~led;
__delay_cycles(400000);
led = led<<1;
if (led == 0)
led = 1;
}
return 0;
}
Надеюсь все понятно. Нажимаем F7. Ищем файл прошивки в директории проекта. Грузим в микроконтроллер и наслаждаемся бегущим огоньком.
Comments
Заставьте диод бегать в одну сторону, затем в другую в цикле.
Или типа того. :)
Разьве int не естественнее?
Порт ввода-вывода AVRa - тоже 8-ми разрядный. Зачем заводить двух байтную переменную для led, если использоваться будет только половинка?
Задания,если есть возможность, добавьте.Кто хочет научиться будет выполнять.Атам появятся новые вопросы,обсужде ния,пути решения задач.Для обучения лучше не придумаешь.
Большое Вам спасибо.
ошибочка, на 2 в степени n
у меня тут есть вопрос по поводу сдвига байта в право
вот код
Code:
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
int main(void)
{
unsigned char led = 255;
DDRB = 255;
while(1)
{
PORTB = ~led;
_delay_ms(150);
led = led>>1;
if (led == 0)
led = 255;
}
return 0;
}
так вот не загорается первый светодиод PBO я подумал из-за результата “деления” прочитал в вашей статье про макроопределени я
так я не понял из-за результата “деления” или в коде накосячил
Поменяй две строчки местами и PB0 будет зажигаться.
Code:
if (led == 0) led = 255;
led = led>>1;
но мне не понятно почему мой код после изменения местами двух строчек и PB0 заработал
Я в программировани и начинающий хочу знать на будующее
#include <ioavr.h>
#include <intrinsics.h>
int main(void)
{
unsigned char led = 1;
DDRB = 255;
while(1){
do {
__delay_cycles(300000);
PORTB = led;
led = led<<1;}
while (led!=128);
do {
__delay_cycles(300000);
PORTB = led;
led = led>>1;}
while (led!=1);
}
return 0;}
Материал закреплен. Хотелось бы видеть статью про синтаксис СИ в IAR вкратце по всем конструкциям и выражениям, желательно с примерами.
unsigned char tmp = 1;
tmp = tmp
#include
#include
int main(void)
{
unsigned char led = 1;
DDRC = 255;
while(1){
if (led!=0){
PORTC = ~led;
delay_ms(100);
led = led1;}
}
return 0;
#include
int main(void)
{
unsigned char led = 1;
DDRC = 255;
while(1){
if (led!=0){
PORTC = ~led;
delay_ms(100);
led = led1;}
}
return 0;
Quote:
RSS feed for comments to this post