Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

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 – они предназначены для настройки маски прерываний и флагов прерываний.

Все перечисленные регистры можно использовать для всех трех таймеров. Для таймера TC1 есть возможность использовать расширенные настройки в связи с его 16-битной структурой.

Данный раздел не претендует на полноту описания работы с таймерами и ШИМ.

3.1. Программа с ШИМ

Для начала построим в Proteus схему и дальше применим ее для моделирования

ATmega328 PWM

Теперь программа.

#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 для эмуляции процесса.

ATmega328 SPI MAX7221

Программировать будем вывод данных на 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)                //Функция передачи двух пакетов по 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 проходит по следующей схеме:

  1. Выставляем SS в 0;

  2. Заносим данные в регистр SPDR данные;

  3. Каждый бит по очереди передается по тактам SCK;

  4. Завершаем передачу установкой SS в 1.

Часть 4

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

Теги:

Опубликована: Изменена: 04.06.2017 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (2) | Я собрал (0) | Подписаться

+1
Ваше имя #
Почему то ни в одном туториале по spi не упоминается одна очень важная вещь. Я по своей простоте хотел обойтись только выводом данных (MOSI PB3 ) и линией тактирования (SCK PB5),ну а зачем мне больше ?прижимал к земле устройство другим пином (я говорю про так называемый SS). И вот собственно в чем заковырка, настраиваю я значит порт DDR на выход только для MOSI и SCK, собираю код-не работает, танцую с бубном - не работает. Почему? Потому что какая особенность МК (может китайского) что если не назначить DDR SS(он же PB2) то spi не заводится. Это стоило мне очень много времени и нервов
Ответить
0

[Автор]
vavaav #
Я просто брал даташит на контроллер и там расписан порядок регистров для инициализации. Для AVR очень доходчиво в даташите расписано с примерами на си и ассемблере.
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется сила тока?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
LC-измеритель LC100-A DC-DC регулируемый преобразователь 1.5-37В 2А с индикатором
вверх