
- •Основы языка си для микроконтроллеров avr
- •1. Основы языка Си для микроконтроллеров avr
- •Препроцессор языка Cи его команды
- •Директива #include
- •Директивы #define, #undef
- •Директивы #if, #ifdef, #ifndef, #else и #endif
- •Встроенные макросы
- •Директива #line
- •Директива #error
- •Директивы #asmи #endasm
- •Зарезервированные ключевые слова
- •Идентификаторы
- •Комментарии
- •Константы
- •Переменные
- •Массивы
- •Типы данных
- •Операнды и операции
- •Унарные операции
- •Бинарные операции
- •Приоритеты операций и порядок вычислений
- •Операторы
- •If-else
- •Оператор for
- •Do-while
- •Continue
- •Оператор-выражение
- •Пустой оператор
- •Составной оператор
- •Функции
- •Указатели
- •Доступ к регистрам ввода/вывода
- •Побитовый доступ к регистрам ввода/вывода
- •Доступ к eeprom-памяти
- •Использование прерываний
- •Организация памяти sram
Доступ к регистрам ввода/вывода
Каждому порту в микроконтроллере AVRсоответствуют минимум три регистра:
• DDRx— регистр направления данных (х — буква соответствующего порта, например:DDRB— регистр направления данных Порта В). Этот регистр определяет, как должен быть сконфигурирован вывод порта — как вход или как выход. Если в бит этого порта записать 1, то соответствующий вывод порта будет работать как выход; если записать 0, то соответствующий вывод порта будет работать как вход. Например, если в регистреDDRBзаписано число 01000011, то это значит, что 0-й, 1-й и 6-й выводы Порта В сконфигурированы как выходы, а остальные — как входы.
• PORTx— выходной регистр порта х (например,PORTB— выходной регистр Порта В). В биты этого регистра записывают те значения, которые хотят получить на выводах порта, при условии, что выводы сконфигурированы как выходы.
Если вывод сконфигурирован как вход, то при записи в соответствующий бит регистра PORTx1 этот вывод становится «входом с подтяжкой», т. е. этот вывод через резистор примерно 100... 150 кОм, находящийся внутри микроконтроллера, подключается («подтягивается») к напряжению питания микроконтроллера. Таким образом, при отсутствии сигнала на этом входе будет уровень логической 1.
• PINx— выводы порта х (например,PINB— выводы Порта В). Этот регистр содержит значения логических уровней, которые к настоящему времени присутствуют на физических (т. е. реальных) выводах порта. Этот регистр доступен ТОЛЬКО для чтения.
Для доступа к регистрам ввода/вывода микроконтроллера AVRс помощью инструкций ассемблераINиOUTкомпилятор использует ключевые словаsfrbиsfhv.
Пример:
/* Доступ к регистрам ввода/вывода */
/* Определим SFR (Регистры общего назначения) для чипа AT90S8515 */
sfrb PINC=0xl3; // Так как регистр PINA 8-битный, то к нему осуществляется
// 8-битный доступ с помощью sfrb (sfr byte)
// 0x13 — это адрес порта PINC sfrw TCNTl=0x2c;
// так как регистр TCNT1 16-битный, то к нему
// осуществляется 16-битный доступ с
// помощью sfrw (sfr word)
// 0x2с - это адрес регистра TCNT1
/* Основная функция программы */
void main(void) {
unsigned char x; // Объявляем символьную переменную x
x=PINC; // Чтение выводов Порта А
TCNTl=0xF4A6; // Запись в регистры TCNT1L & TCNT1H
}
Адреса регистров ввода/вывода определены в заголовочных файлах.
В начале программы посредством #include можно подключить соответствующий файл для того микроконтроллера, который используется в данном проекте.
Побитовый доступ к регистрам ввода/вывода
Побитовый доступ к регистрам ввода/вывода осуществляется путем добавления выбранных битов после имени регистра.
Поскольку побитовый доступ к регистрам ввода/вывода осуществляется с использованием инструкций CBI,SBI,SBICиSBIS, адрес регистра должен быть в диапазоне 0...1Fhдляsfrbи в диапазоне 0...1Ehдляsfrw.
Пример:
/* Доступ к регистрам ввода/вывода */
/* Определим SFR (Регистры общего назначения) для чипа AT90S2313 */
sfrb PORTB=0xl8; // Объясняем компилятору, по каким адресам находятся
sfrb DDRB=0xl7; //те или иные регистры (порты). Так как все используемые
sfrb PINB=0xl6; // регистры 8-битные, то к ним осуществляется
// 8-битовый доступ с помощью sfrb. Обычно эти
// операторы располагаются в заголовочных файлах
// (см. Доступ к регистрам ввода/вывода)
int a, b, с; // Объявляем целые переменные а, b, с
/* Основная функция программы */
void main(void)
{
/* Зададим направление выводов Порта В */
DDRB.0=0 DDRB.1=0 DDRB.2=0 DDRB.3=0 // зададим биты 0...3 порта В как
// входы
DDRB.4=1 DDRB.5=1 DDRB.6=1 DDRB.7=1 // зададим биты 4...7 Порта В как
// выходы
/* Установим выводы Порта В */
PORTB.0=0; PORTB.1=0; // установим биты 0 и 1 Порта В как простые входы
PORTB.2=1; PORTB.3=1; // установим биты 2 и 3 Порта В как "входы с подтяжкой", т. е. каждый из этих выводов через резистор примерно 100...150 кОм будет соединён с шиной питания. Таким образом, в отсутствие сигнала на этих выводах будет уровень логической 1.
PORTB.4=0; PORTB.5=0; // установим биты 4 и 5 Порта В как выходы, на которых установлен уровень логического 0
P0RTB.6=1; P0RTB.7=1; // установим биты 6 и 7 Порта В как выходы, на которых установлен уровень логической 1
/* опросим выводы Порта В, т. е. их физические значения */
a=PINB.0; // Переменной а присвоим значение вывода 0 Порта В, т. е. значение сигнала, на выводе 0
b=PINB.5; // Переменной b присвоим значение вывода 5 Порта В, т. е. b=0
c=PINB.6; // Переменной с присвоим значение вывода 6 Порта В, т. е. с=1
if (PINB.2) { // Если на выводе 2 Порта В логическая 1,
[группа операторов 1] // то выполняется [группа операторов 1];
}
else
[группа операторов2] // если 0 - то [группа операторов2]
}
Чтобы улучшить удобочитаемость программы, битам в регистрах ввода/вывода можно присвоить символические имена посредством #define.
Пример:
/* Присвоение символических имен битам регистров ввода/вывода */
/* Эта примитивная программа зажигает светодиод по нажатию кнопки. Чип: AT90S2313. Светодиод подключён катодом к выводу 0 Порта В, а анодом через резистор 470 Ом — к плюсу источника питания. Кнопка одним выводом подключена к выводу 1 Порта В, а другим - к минусу источника питания (к "общему"). Частота кварца 1 МГц, хотя в данном случае она может быть любой */
#include <90s2313.h> // подключаем файл, где определены адреса регистров
/* Присвоение символических имен */
#define output_led PORTB.0 // Выход на светодиод
#define input_button PINB.1 // Вход кнопки
/* Основная функция программы */
void main(void)
{
/* Зададим направление выводов Порта В */
DDRB.0=1; // зададим бит 0 Порта В как выход
DDRB.1=0; // зададим бит 1 Порта В как вход
/* Установим выводы Порта В */
output_led =1; // используем символическое имя бита PORTB.0, запишем в него 1, чтобы установить вывод 0 Порта В как выход, на котором установлен уровень логической 1, чтобы светодиод не горел
P0RTB.1=1; // установим бит 1 Порта В как "вход с подтяжкой", чтобы при отжатой кнопке на этом выводе была 1, а при нажатии кнопки уровень менялся на 0
/* Бесконечный цикл */
while (1) // Оператор цикла
{
/* Тело цикла while */
if (input_button==0) // В условии используем символическое
// имя вывода PINB.1. Если кнопка нажата,
output_led=0; // т. е. на выводе PINB.1 логический 0, то зажигаем светодиод
else
output_led=l; // Иначе, если кнопка отжата,гасим светодиод
} // Скобка закрывает цикл while()
} // Скобка закрывает функцию main()
Важно отметить, что побитовый доступ для регистров ввода/вывода, расположенных во внутреннем SRAMвыше адреса 5Fh(например, Порт Е дляATmegal28), РАБОТАТЬ НЕ БУДЕТ, поскольку ассемблерные инструкцииCBI,SBI,SBICuSBISне могут быть использованы для доступа к SRAM.