Главная » Микроконтроллеры
Призовой фонд
на октябрь 2017 г.
1. Термометр Relsib WT51
Рэлсиб
2. 1000 руб
PCBWay
3. Регулируемый паяльник 60 Вт
Паяльник
4. 100 руб.
От пользователей

AvrStudio 4. CMSIS для AVR. Структура для GPIO

1. Введение

Я начал изучать STM8 микроконтроллеры. Для начала скачал библиотеку для STM8L152C6 (на али я закал Discovery-STM8L152C6T6 ) и начал смотреть и вникать как она реализована. Библиотека написана в формате CMSIS. Соответственно и доступ к ней аналогичен STM32. Если научился работать с ней,  то ARM пойдет как по маслу. Но AVR держит крепко - привык.

Поэтому решил по аналогии с STM8 написать свой вариант CMSIS для AVR.

2. Обзор CMSIS для STM8

Библиотеку для STM8 открывал AvrStudio4.

Структура библиотека и файлов стандартны для CMSIS. Для того чтобы эффективно пользоваться необходимо понимать что, откуда берется. Итак в файле stm8l15x.h существует следующая структура на примере порта GPIO:

1. В начале определения volatile для отключения оптимизатора.

определения volatile для отключения оптимизатора

2. Затем идет описание структуры для GPIO.

описание структуры для GPIO

Ссылка на структуру указывает на адрес первого регистра ODR

3. Затем идут адреса первых регистров для периферии

адреса первых регистров для периферии 

4. А дальше идет ассоциация структуры и адреса

ассоциация структуры и адреса

3. Обращения к структурам

Рассмотрим виды В зависимости как произведено объявление структуру отличаются обращения к ней.

3.1. Если бы мы объявили структуру как

#define GPIOA    *( (GPIO_TypeDef *) GPIOA_BASE )

То обращение происходило бы по значению в формате 

GPIO.ODR=0xFF;

3.2. Как вы поняли уже в нашем случае объявление произведено по указателям, поэтому обращение к структуре производится в формате

GPIO->ODR=0xFF;

Второй вариант является более предпочтительным, т.к. при передаче в функцию структуры объявленной по значению пришлось бы копировать в оперативную память в нашем случае 3 байт, а при обращении через указатель только адрес первого элемента 2 байта, причем в этом случае не важно сколько элементов будет в структуре. 

4. Портирование структуры на AVR

Для того, чтобы работать с периферией через структуру необходимо, чтобы адреса регистров упорядочить по адресу. Порты общего ввода-вывода подходят для нашего учебного примера лучше всего. А затем в структуре ввести их в том же порядке.

Для примера я взял порт D, но с другими портами ситуация аналогична. Начинается с адреса 0х30, а заканчивается 0х31.

#include <avr/io.h>
/**
 * IO definitions
  * define access restrictions to peripheral registers
 */
#define   __I    volatile const  /*!< defines 'read only' permissions   */
#define   __O   volatile    /*!< defines 'write only' permissions   */
#define   __IO   volatile    /*!< defines 'read / write' permissions  */

// структурный тип данных
typedef struct
{
 __I uint8_t pin;
 __IO uint8_t ddr;
 __IO uint8_t port;
} GPIO_t;
#define GPIOD ((GPIO_t *) &PIND)  //  создаем указатель на структуру порта D
#define GPIOC ((GPIO_t *) &PINC)  // создаем указатель на структуру порта C

int main(void)
{
GPIOD->ddr=0xFF; // определяем порт на выход

 while(1)
 {
 GPIOD->port+=1;  // увеличиваем счетчик на 1

 }

}

Определения для volatile оставляем без изменений. ЕСЛИ ЗАБУДЕТЕ volatile  ДОБАВИТЬ В ВАШУ СТРУКТУРУ, ТО КОМПИЛЯТОР ОПТИМИЗИРУЕТ ЗАПИСЬ БЕЗ ПРЕДУПРЕЖДЕНИЙ (УДАЛИТ) и вы будите голову ломать почему не работает. Создаем новый тип данных для нашей структуры. Порядок перечисления должен совпадать с адресным порядком соответствующих регистров. Далее, создаем указатель на структуру портов, проводим инициализацию, и мигаем портом.

5. Структура и битовые поля

Удобства от структуры которая была сделана выше нет. Мы назвали порты GPIO длиннее. Если необходимо обращаться к отдельным битам, для считывания, или просто пином "помигать", то приходиться городить битовые маски, например: 

PORTC|=(1<<7);

PORTC&=~(1<7);

Считаю, данные записи не очень удобными для работы. Для устранения данного недостатка применим битовые маски.

Напишем программу которая мигает на порте С, если высокий уровень на 7 пине, то пин 2  будет мигать, иначе мигаем 4 пином.

#include <avr/io.h>
/**
 * IO definitions
 *
 * define access restrictions to peripheral registers
 * определения ограничения доступа к периферийным регистрам
 */
#define   __I   volatile const  /*!< "только чтение" */
#define   __O   volatile    /*!< "только запись" */
#define   __IO   volatile    /*!<  "чтение/запись" */

// typedef struct
{
 __I uint8_t pin0:1;
 __I uint8_t pin1:1;
 __I uint8_t pin2:1;
 __I uint8_t pin3:1;
 __I uint8_t pin4:1;
 __I uint8_t pin5:1;
 __I uint8_t pin6:1;
 __I uint8_t pin7:1;
//-------------
 __IO uint8_t ddr0:1;
 __IO uint8_t ddr1:1;
 __IO uint8_t ddr2:1;
 __IO uint8_t ddr3:1;
 __IO uint8_t ddr4:1;
 __IO uint8_t ddr5:1;
 __IO uint8_t ddr6:1;
 __IO uint8_t ddr7:1;
//------------
 __IO uint8_t port0:1;
 __IO uint8_t port1:1;
 __IO uint8_t port2:1;
 __IO uint8_t port3:1;
 __IO uint8_t port4:1;
 __IO uint8_t port5:1;
 __IO uint8_t port6:1;
 __IO uint8_t port7:1;
} GPIO_t;

//---------------------------------
#define GPIOD  ((GPIO_t *) &PIND)
#define GPIOC  ((GPIO_t *) &PINC)

//----------------
int main(void)
{
// инициализация направление ввода
GPIOC->ddr4=1; //  пин 4 на вывод
GPIOC->ddr2=1; //  пин 2 на вывод
GPIOC->ddr7=0; //  пин 7 на ввод

while(1)
{
if (GPIOC->pin7)
   {
   GPIOC->port2=1;
   GPIOC->port2=0;
   }
 else
  {
   GPIOC->port4=1;
   GPIOC->port4=0;
   }
}

}

Данный формат считаю более удобным. Программа написана без битовых масок. Данная программа, можно сказать, написана в формате CMSIS.

Для полного счастья осталось убрать магические числа введя числовые определения. Предположим нужно объявить целый порт на выход. Что же нам теперь по одному биту выставлять? Данный недостаток мы решим добавив ключевое слово UNION. Про него очень хорошо написано в статье

#include <avr/io.h>
/**
 * IO definitions
 *
 * определения ограничения доступа к периферийным регистрам
 */
#define   __I   volatile const  /*!< 'только чтение' разрешения  */
#define   __O   volatile    /*!< 'только запись' разрешения  */
#define   __IO   volatile    /*!< 'чтение/запись' разрешения  */

// тип для PORT
typedef enum {
 lo=0, // отключаем подтягивающий резистор
 hi=1  // включаем подтягивающий резистор   } hi_lo;
// //тип для DDR
typedef enum {
 in=0, // определяем порт на вход
 out=1 // определяем на выход   } in_out;
// typedef struct
{
union
{
 __I  uint8_t pin;
 struct
 {
 __I uint8_t pin0:1;
 __I uint8_t pin1:1;
 __I uint8_t pin2:1;
 __I uint8_t pin3:1;
 __I uint8_t pin4:1;
 __I uint8_t pin5:1;
 __I uint8_t pin6:1;
 __I uint8_t pin7:1;
 };
};
//-------------
union
{
 __IO uint8_t ddr;
 struct
 {
 __IO in_out ddr0:1;
 __IO in_out ddr1:1;
 __IO in_out ddr2:1;
 __IO in_out ddr3:1;
 __IO in_out ddr4:1;
 __IO in_out ddr5:1;
 __IO in_out ddr6:1;
 __IO in_out ddr7:1;
 };
};
//------------
union
{
 __IO uint8_t port;
 struct
 {
 __IO hi_lo port0:1;
 __IO hi_lo port1:1;
 __IO hi_lo port2:1;
 __IO hi_lo port3:1;
 __IO hi_lo port4:1;
 __IO hi_lo port5:1;
 __IO hi_lo port6:1;
 __IO hi_lo port7:1;
 };
};
} GPIO_t;

//---------------------------------
#define GPIOD  ((GPIO_t *) &PIND)
#define GPIOC  ((GPIO_t *) &PINC)

//----------------
int main(void)
{
// инициализация направления ввода
GPIOC->ddr4=out; // пин 4 на вывод
GPIOC->ddr2=out; // пин 2 на вывод
GPIOC->ddr7=in; // пин 7 на ввод

while(1)
{
if (GPIOC->pin7)
   {
   GPIOC->port2=hi; // включаем подтяг.резистор
   GPIOC->port2=lo; // отключаем подтяг.резистор
   GPIOC->port=0x0F;
   GPIOC->port=0;
   }
 else
  {
   GPIOC->port4=hi; // включаем подтяг.резистор
   GPIOC->port4=lo; // отключаем подтяг.резистор
   GPIOC->port=0xF0;
   GPIOC->port=0;
   }
}

}

Надеюсь, что статья получилась познавательной и полезной, как для тех кто изучает AVR, так и для перехода на более современные МК STM8, STM32 или ARM другого изготовителя, принципиальной разницы нет. Для более полного понимания процессов запустите программу в симуляторе AvrStudio4 и пощелкайте на F11, прочувствуйте как четко и красиво работает этот подход.

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

Теги:

Опубликована: 0 0
Я собрал 0 1
x

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

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

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

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

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
+1
vmouse #
Занимался похожим, но дошел только до работы с битами через структуру. И union тоже юзал.
Но вот так коротко и понятно объяснить бы не смог. Спасибо, очень полезно!
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Катушка Тесла Модуль измерения тока на ACS712 (30А)
вверх