3. Аппаратный ШИМ (PWM).
3.1. Таймеры/счетчики и регистры управления.
ШИМ (широтно импульсная модуляция) в микроконтроллере реализуется с использованием аппаратных таймеров. Теорию ШИМ рассматривать не будем. Рассмотрим лишь получение плавного изменения напряжения/тока на основе ШИМ. Для начала разберемся как управлять таймерами.
Имеется три таймера два по 8 бит (TC0, TC2) и один 16 бит (TC1). Настройка происходит регистрами TCCRxA, TCCRxB(x – означает выбранный таймер 0, 1 или 2).
Разберем чуточку подробнее на примере таймера TC1 в режиме 8 бит:
Регистр TCCR1A
бит
7
6
5
4
3
2
1
0
COM1A1
COM1A0
COM1B1
COM1B0
–
–
WGM11
WGM10
Бит 7, 6 (COM1A1, COM1A0)- настройка поведения вывода A по событию.
Бит 5, 4 (COM1B1, COM1B0)- настройка поведения вывода B по событию.
COM1A1/
COM1B1
COM1A0/
COM1B0
События
0
0
Нормальная работа порта, таймер отключен от вывода.
0
1
Переключение порта с 0 на 1 или обратно при совпадении.
1
0
Сброс на 0 при совпадении.
1
1
Установка 1 при совпадении.
Бит 3, 2 — зарезервированы.
Бит 1, 0 (WGM11, WGM10) — позволяют настроить ШИМ, подробно рассматривать не будем из-за объемности описания.
Регистр TCCR1B
бит
7
6
5
4
3
2
1
0
FOC1A
FOC1B
–
–
WGM12
CS12
CS11
CS10
Бит 7, 6 (FOC1A, FOC1B)- при использовании ШИМ должны быть установлены в 0.
Бит 5, 4 — зарезервированы.
Бит 3 — используется совместно с WGM11, WGM10 для настройки ШИМ.
Бит 2, 1, 0 (CS12, CS11, CS10) — выбор источника тактирования таймера.
CS12
CS11
CS10
Событие
0
0
0
Нет тактового сигнала (таймер остановлен)
0
0
1
CLK
0
1
0
CLK/8
0
1
1
CLK/64
1
0
0
CLK/256
1
0
1
CLK/1024
1
1
0
Внешний тактовый импульс по заднему фронту.
1
1
1
Внешний тактовый импульс по переднему фронту.
Регистр TCNT1
бит
7
6
5
4
3
2
1
0
–
–
–
–
–
–
–
–
Из регистра TCNT1 можно считать значение или записать начальное значение для таймера.
Регистры OCN1A и OCN1B
бит
7
6
5
4
3
2
1
0
–
–
–
–
–
–
–
–
Регистры OCN1A и OCN1B содержат значение сравнения связанные с соответствующими выводами микроконтроллера.
Есть ещё два регистра TIMSK1 и TIFR1 – они предназначены для настройки маски прерываний и флагов прерываний.
Все перечисленные регистры можно использовать для всех 3-х таймеров. Для таймера TC1 есть возможность использовать расширенные настройки в связи с его 16-битной структурой.
Данный раздел не претендует на полноту описания работы с таймерами и ШИМ.
3.1. Программа с ШИМ
Для начала построим в Proteus схему и дальше применим ее для моделирования
Теперь программа.
#include <avr/io.h> //Подключаем библиотеку ввода/вывода AVR
#include <util/delay.h> //Подключаем библиотеку формирования задержки выполнения
int main(void)
{
DDRB |= 1<<PB1; //Устанавливаем как вывод регистр 1 порта B
TCCR1A |= 1<<(COM1A1) | 1<<(WGM10); //В регистре TCCR1A устанавливаем биты COM1A1 и WGM10 в 1
TCCR1B |= 1<<(CS10) | 1<<(WGM12); //В регистре TCCR1B устанавливаем биты CS10 и WGM12 в 1
int pwm = 0; //Объявляем переменную для значения сравнения
int up = 1; //Создаем флаг для увеличения или уменьшения скважности ШИМ
while(1)
{
OCR1A = pwm; //Заносим значение в регистр сравнения
pwm += up ? 1 : -1; //В зависимости от флага uo увеличиваем или уменьшаем значение сравнения pwm
if (pwm == 255) //Проверяем pwm на достижение 255 и переключаем флаг uo в 0
up = 0;
else if (pwm == 0) //Проверяем pwm на достижение 0 и переключаем флаг uo в 1
up = 1;
_delay_ms(10); //Задержка выполнения 10мс
}
return 0;
}
Программа подключает ШИМ к регистру 1 порта B и плавно увеличивает а далее уменьшает ток.
4. Обмен данными по SPI
Вот и добрались до обмена данными между микроконтроллером и внешним миром. Рассмотрим это на связке AVR >> SPI >> MAX7221 >> семисегментные индикаторы.
4.1. SPI
Работу с протоколом обмена SPI на ATmega328 начнем с рассмотрения регистров управления. Всего используется три регистра SPCR, SPSR и SPDR.
Регистр SPCR
бит
7
6
5
4
3
2
1
0
SPIE
SPE
DORD
MSTR
CPOL
CPHA
SPR1
SPR0
Бит 7 (SPIE)- разрешает прерывания.
Бит 6 (SPE)- включает аппаратный SPI.
Бит 5 (DORD) — определяет порядок передачи данных (0 — старший бит вперед, 1 — младший бит вперед).
Бит 4 (MSTR) — режим работы master/slave (1/0).
Бит 3 и 2 (CPOL, CPHA) — режим работы SPI.
Mode
CPOL
CPHA
SPI Mode 0
0
0
SPI Mode 1
0
1
SPI Mode 2
1
0
SPI Mode 3
1
1
Бит 1 и 0 (SPR1, SPR0) — частота тактирования SPI.
SPI2X
SPR1
SPR0
Частота тактирования
0
0
0
f/4
0
0
1
f/16
0
1
0
f/64
0
1
1
f/128
1
0
0
f/2
1
0
1
f/8
1
1
0
f/32
1
1
1
f/64
Регистр SPSR
бит
7
6
5
4
3
2
1
0
SPIF
WCOL
—
—
—
—
—
SPI2X
Бит 7 (SPIF)- флаг прерываний SPI.
Бит 6 (WCOL)- флаг ошибки записи.
Бит 5, 4, 3, 2, 1 — зарезервированы.
Бит 6 (SPI2X)- бит удвоения скорости записи.
Регистр SPDR
бит
7
6
5
4
3
2
1
0
MSB
—
—
—
—
—
—
LSB
Регистр SPDR предназначен для записи/чтения в сдвиговый регистр что приводит к активации передачи данных по SPI.
4.2. Использование SPI.
Рассмотрим передачу данных по SPI на примере вывода информации на семисегментные индикаторы. Для начала создадим схему в Proteus для эмуляции процесса.
Программировать будем вывод данных на 5 семисегментных индикатора через микросхему MAX7221, которая принимает данные по 16 бит по протоколу SPI.
/* Использование семисегментного индикатора с общим катодом и сдвиговым регистром по протоколу SPI */
#include <avr/io.h> //подключаем библиотеки
#include <util/delay.h>
#define SPI_DDR DDRB //назначаем имя для изменеия направления на порту B
#define SPI_PORT PORTB //назначаем имя для используемого порта
#define SPI_SS PB2 //назначаем имя выхода SS
#define SPI_MOSI PB3 //назначаем имя выхода MOSI
#define SPI_MISO PB4 //назначаем имя выхода MISO
#define SPI_SCK PB5 //назначаем имя выхода SCK
char d[18] ={ //кодирование знаков для семисегментного индикатора
0x7E, //0
0x30, //1
0x6D, //2
0x79, //3
0x33, //4
0x5B, //5
0x5F, //6
0x70, //7
0x7F, //8
0x7B, //9
0x77, //a
0x1F, //b
0x4E, //c
0x3D, //d
0x4F, //e
0x47, //f
0x80, //.
0x00 //пусто
};
void spi(char cmd,char data) //Функция передачи 2-х пакетов по 8 бит по протоколу SPI
{
SPI_PORT &= ~(1<<SPI_SS); //сбрасываем SS в 0
SPDR = cmd; //отправляем данные по SPI адрес
while(!(SPSR&(1<<SPIF))); //ждем окончания отправки
SPDR = data; //отправляем данные по SPI данные
while(!(SPSR&(1<<SPIF))); //ждем окончания отправки
SPI_PORT |= (1<<SPI_SS); //устанавливаем SS в 1
}
void clrdig () //Функция очистки индикаторов
{
spi(0x01,d[17]);
spi(0x02,d[17]);
spi(0x03,d[17]);
spi(0x04,d[17]);
spi(0x05,d[17]);
}
int main()
{
SPI_DDR = (1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_SS); //настраиваем MOSI, SCK, SS как выходы
SPI_PORT |=(1<<SPI_SS); //устанавливаем SS в 1
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0); //через регистр SPCR настраиваеи аппаратное SPI
//Инициализация MAX7221
spi(0x0C,0x00); //Отключение индикаторов
spi(0x09,0x00); //Отключение декодирования
spi(0x0A,0x0A); //Интенсивность свечения индикаторов
spi(0x0B,0x04); //Количество индикаторов начиная с 0
spi(0x0F,0x00); //Отключение теста индикаторов
spi(0x0C,0x01); //Включение индикаторов
clrdig(); //Очистка всех индикаторов
int i=0; //Переменная перебора выводимых символов
int j=1; //Переменная осчета номера индикатора
while(1){ //Бесконечный цикл
spi(j,d[i]); //Вывод на индикатор j символа i
_delay_ms(1000); //Задержка выполнения 1000мс
i++;
if(i>17)i=0;
j++;
if(j>5)j=1;
}
return 0;
}
Перед использованием MAX7221 не забываем провести инициализацию.
Передача по SPI проходит по следующей схеме:
Выставляем SS в 0;
Заносим данные в регистр SPDR данные;
Каждый бит по очереди передается по тактам SCK;
Завершаем передачу установкой SS в 1.
Прикрепленные файлы:
- proteus_pwm.zip (14 Кб)
- CB_pwm.zip (6 Кб)
- spi_P.zip (16 Кб)
- s_sg_katod.zip (31 Кб)