AVR на C — просто? Часть 3

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.

  • Часть 4

    Прикрепленные файлы:

    Добавить комментарий

    Ваш адрес email не будет опубликован.