В начало | Зарегистрироваться | Заказать наши киты почтой
 
 
 
 

Программирование микроконтроллеров на языке C. Часть 4

📆5 февраля 2024   ✒️erbol   🔎503   💬0  
Программирование микроконтроллеров на языке C. Часть 4

Доброго дня всем жителям и гостям Датагор.ру! Сегодня мы средствами С реализуем проект из моей статьи «Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК» , суть которого заключается в управлении двумя светодиодами:
а) жёлтый мигает с видимой для глаза частотой, задаваемой таймером.
б) на зелёный подаётся ШИМ-сигнал, скважность которого определяется величиной аналогового напряжения на входе АЦП микроконтроллера, причём период измерений напряжения задаётся тем же таймером.
Чтобы облегчить переход от одного языка программирования к другому тем из вас, кто пришёл к этой работе через статью «Ассемблер...»:
• оставим без изменений структуру проекта, а также имена функций и макроопределений битов,
• С-код будем, по мере возможности, оформлять внешне похожим на приведённый в упомянутой статье, используя макросы setBit, clearBit и toggleBit,
• одни и те же фрагменты программы на двух языках будут приводиться рядом,
• выберем для обоих случаев одинаковые пины и модули МК, указанные в Таблице 1 ниже, подключив при этом светодиоды для работы в активно-высоком режиме.
Программирование микроконтроллеров на языке C. Часть 4
Таблица 1. Модули и пины МК, используемые в проекте

Создадим папку проекта lightControl и скопируем в неё шаблонные файлы. Кроме того, создадим файлы в папке:
inc
• macro.h
• led.h
• timer.h
• adc.h
• pwm.h (только для nRF52832)
src
• led.с
• timer.с
• adc.с
• pwm.c (только для nRF52832)
• main.c

Макросы для работы с битами

Пропишем в macro.h вышеуказанные макросы.
Atmega8
#ifndef MACRO_H_
#define MACRO_H_

#define setBit(arg1, arg2)      arg1 |= (1 << arg2)
#define clearBit(arg1, arg2)    arg1 &= ~(1 << arg2)
#define toggleBit(arg1, arg2)   arg1 ^= (1 << arg2)

#endif

STM32F401
#ifndef MACRO_H_
#define MACRO_H_

#define setBit(arg1, arg2)      arg1 |= arg2
#define clearBit(arg1, arg2)    arg1 &= ~arg2
#define toggleBit(arg1, arg2)   arg1 ^= arg2

#endif

nRF52832
#ifndef MACRO_H_
#define MACRO_H_

#define setBit(arg1, arg2)      arg1 |= (1UL << arg2)
#define clearBit(arg1, arg2)    arg1 &= ~(1UL << arg2)
#define toggleBit(arg1, arg2)   arg1 ^= (1UL << arg2)

#endif

Как видите:
1) Макросы для ATmega8 и nRF52832 отличаются лишь суффиксом UL после единицы в маске. Подробнее о необходимости такого суффикса мы говорили во второй части статьи.
2) Для STM32F401 макросы принимают в качестве аргумента arg2 не номер бита, как в случае с ATmega8 и nRF52832, а маску с единицей в этом бите. Обусловлено это тем, что во вспомогательных хэдер-файлах (avr/io.h. stm32f4xx.h, nrf.h и подключаемые ими) для ATmega8 и nRF52832 даны, среди прочего, и макроопределения номеров битов, а для STM32F401 — только макроопределения масок с единицей в заданном бите.

Программа для ATmega8

Порты ввода-вывода ATmega8

Оформим функции:
• инициализации пинов PB1 и PB2 в качестве выходов путём установки в 1 соответствующего бита регистра DDRB,
• включения, выключения и переключения жёлтого светодиода путём записи 1, 0 и противоположного значения, соответственно, во второй бит регистра PORTB.

Рисунок 1. Хэдер-файл led для ATmega8 на языках а) С и б) ассемблер



Рисунок 2. Исходный файл led для ATmega8 на языках а) С и б) ассемблер


Таймер ATmega8

На Рисунке 3 представлены выдержки из Раздела «16-bit Timer/Counter1» даташита, где красным цветом помечена полезная для нас информация.

Рисунок 3. Выдержки из даташита для настроек TIMER1 ATmega8


В функции инициализации таймера нам необходимо:
1. Выбрать 8-битный режим Fast PWM, для чего нужно установить в 1 биты WGM12 регистра TCCR1B и WGM10 регистра TCCR1A. Выбор разрядности в восемь бит счётчика таймера обусловлен тем, что именно столько разрядов регистра данных АЦП мы будем использовать для установки текущей скважности ШИМ-сигнала.
2. Разрешить ШИМ-сигнал на пине OC1A (PB1), т.е. на выводе управления зелёным светодиодом, записав единицу в бит COM1A1 регистра TCCR1A.
3. Записать 0 в регистр OCR1A, значение которого (от 0 по 255) определяет рабочий цикл (от 0% по 100%, соответственно) ШИМ-сигнала.
4. Разрешить прерывание таймера по переполнению счётчика, установив в 1 бит TOIE1 регистра TIMSK.
5. Включить тактирование таймера с делителем 64, записав 1 в биты CS11 и CS10 регистра TCCR1B. В этом случае мы получим период прерывания 64 * 255 / 8 МГц = 2 мс.

В обработчике же прерывания:
• запустить измерение аналогового напряжения,
• инкрементировать переменную-счётчик и при достижении ею значения 200 (т.е. каждые 2 мс * 200 = 400 мс) переключить жёлтый светодиод в противоположное состояние.


Рисунок 4. Хэдер-файл timer для ATmega8 на языках а) С и б) ассемблер

Обратите внимание, что C-варианте хэдер-файла компилятору посредством ключевого слова extern указывается на использование внешних функций adcStartConversion() и yelowLedToggle().


Рисунок 5. Исходный файл timer для ATmega8 на языках а) С и б) ассемблер


Аналого-цифровой преобразователь ATmega8

Обратимся к данным из Раздела «Analog-to-Digital Converter» даташита, приведённым на Рисунке 6.

Рисунок 6. Выдержки из даташита для настроек АЦП ATmega8


Для работы АЦП в рамках нашего проекта достаточно следующих настроек при инициализации:
1. Выбрать канал ADC1, установив в 1 бит MUX0 регистра ADMUX.
2. Установить выравнивание по левому краю регистра данных АЦП, записав 1 в бит ADLAR регистра ADMUX. Объясняется такой выбор следующим. АЦП ATmega8 — 10-разрядный, поэтому для размещения результата измерения выделена пара сдвоенных регистров ADCH и ADCL. Преобразователь нередко шумит (особенно при дрейфе температуры) в пределах младших разрядов. Поскольку особая чувствительность АЦП нам не требуется, можно пренебречь двумя младшими разрядами, выровняв данные по левому краю и считывая после измерения лишь старший регистр пары — ADCH.
3. Разрешить прерывание АЦП по завершению измерения установкой в 1 бита ADIE регистра ADCSRA.
4. Выбрать частоту тактирования преобразователя. В нашем случае особая скорость не нужна, поэтому выберем делитель 4, записав 1 в бит ADPS1 регистра ADCSRA.
5. Включить АЦП через бит ADEN регистра ADCSRA.

Далее, поскольку режим измерения у нас — одноразовый, а не непрерывный, пропишем функцию adcStartConversion() запуска измерения, который осуществляется установкой в 1 бита ADSC регистра ADCSRA.

И последнее, что осталось — копирование в обработчике прерывания значения ADCH в регистр OCR1A таймера.

Рисунок 7. Хэдер-файл adc для ATmega8 на языках а) С и б) ассемблер



Рисунок 8. Исходный файл adc для ATmega8 на языках а) С и б) ассемблер


Файл main.c для ATmega8

В основной функции необходимо вызвать функции инициализации GPIO, таймера и АЦП, а также глобально разрешить прерывания.

Рисунок 9. Файл main для ATmega8 на языках а) С и б) ассемблер


Программа для STM32F401

Порты ввода-вывода STM32F401

Выдержки из мануала и даташита, имеющие отношение к настройке выводов управления светодиодами, вы можете найти на Рисунке 10, где красным цветом выделена информация, касающаяся обоих пинов, зелёным — PB1, а коричневым — PB2.

Рисунок 10. Выдержки из мануала и даташита для настроек GPIO STM32F401


Функция инициализации состоит из следующих шагов:
1. Включить тактирование порта В, записав 1 в бит GPIOBEN регистра RCC_AHB1ENR.
2. Настроить режим работы пинов через регистр MODER. Поскольку мы используем пины с номерами 2 и 1, необходимо записать требуемые значения в группы битов MODER2 и MODER1 указанного регистра, соответственно:
• 01 — в MODER2, устанавливая режим обычного выхода для PB2,
• 10 — в MODER1, поскольку PB1 будет исполнять альтернативную функцию.
3. Из Таблицы 8 даташита следует, что альтернативной функцией PB1 являются события канала 4 таймера TIMER3, а из Таблицы 9 мы можем узнать номер этой функции — AF02. Далее, нужно определить для данного номера функции значение группы битов AFRL1 (поскольку номер пина — 1) регистра AFRL. В нашем случае это — 0010 или единица в пятом бите регистра.

Кроме того, оформим функции включения, выключения и переключения жёлтого светодиода, что соответствует установке в 1, сбросу в 0 и изменению на противоположное значения бита ODR2 регистра ODR.

Рисунок 11. Хэдер-файл led для STM32F401 на языках а) С и б) ассемблер



Рисунок 12. Исходный файл led для STM32F401 на языках а) С и б) ассемблер


Таймер STM32F401

На Рисунке 13 приведены выдержки из мануала касательно регистров и их битов, участвующих в настройке и работе таймера общего назначения, к которым относится и TIMER3.

Рисунок 13. Выдержки из мануала для настроек таймера STM32F401


В функции инициализации таймера необходимо:
1. Включить тактирование таймера, установив в 1 бит TIM3EN регистра RCC_APB1ENR.
2. Записать в регистр PSC значение 1600 делителя.
3. Установить значение сравнения 255, записав его в регистр ARR. В совокупности с делителем это даст период прерывания в 1600 * 255 / 16 МГц = 25.5 мс. Кроме того, таким образом мы ограничим разрядность счётчика таймера 8 битами, сравняв её с таковой для АЦП.
4. Выбрать для канала 4-го таймера режим PWM1 посредством комбинации 110 битов CC4M регистра CCMR2.
5. Записать в регистр CCER начальное значение 0% для рабочего цикла ШИМ-сигнала.
6. Разрешить ШИМ-сигнал для канала 4 таймера (т.е. на пине PB1), установив в 1 бит CC4E регистра CCER.
7. Разрешить прерывание таймера локально через бит UIE регистра DIER и глобально.
8. Включить таймер, установив в 1 бит CEN регистра CR1.

А в обработчике прерывания:
а) Очистить бит UIF регистра SR.
б) Запустить измерение аналогового напряжения.
в) Инкрементировать переменную-счётчик и, если её значение равно 20 (что даст 25.5 мс * 20 = 510 мс), изменить состояние жёлтого светодиода на противоположное.

Рисунок 14. Хэдер-файл timer для STM32F401 на языках а) С и б) ассемблер


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

Рисунок 15. Исходный файл timer для STM32F401 на языках а) С и б) ассемблер


Аналого-цифровой преобразователь STM32F401

Биты и регистры, ответственные за настройку пина PA1 и АЦП, приведены на Рисунке 16.

Рисунок 16. Выдержки из мануала для настроек АЦП STM32F401


Настройка включает в себя:
1. Разрешение тактирования модуля GPIOA через установку в 1 бита GPIOAEN регистра RCC_AHB1ENR.
2. Выбор для PA1 функции аналогового входа записью комбинации 11 в группу битов MODER1 регистра MODER.
3. Включение тактирования ADC1 путём установки в 1 бита ADC1EN регистра RCC_APB2ENR.
4. Выбор канала IN1 преобразователя посредством записи числа 1 в группу битов SQ1 регистра SQR3.
5. Выбор разрядности в 8 бит через запись комбинации 10 в группу битов RES регистра CR1.
6. Локальное (через бит EOCIE того же регистра CR1) и глобальное разрешение прерывания АЦП по завершению измерения.
7. Включение АЦП с помощью установки в 1 бита ADON регистра CR2.

Функция запуска измерения ограничена записью 1 в бит SWSTART регистра CR2.

В обработчике прерывания надо:
• сбросить в 0 бит EOC регистра SR.
• скопировать в регистр CCR4 таймера значение регистра данных DR АЦП.


Рисунок 17. Хэдер-файл adc для STM32F401 на языках а) С и б) ассемблер



Рисунок 18. Исходный файл adc для STM32F401 на языках а) С и б) ассемблер


Файл main.c для STM32F401

В main() вызываются функции инициализации пинов управления светодиодами, таймера и АЦП.

Рисунок 19. Файл main для STM32F401 на языках а) С и б) ассемблер


Программа для nRF52832

Порт ввода-вывода nRF52832

На Рисунке 20 приведены выдержки из спецификации с регистрами и их битам, участвующими в настройке пина P0.18 GPIO. Однако, вся дальнейшая информация верна и для P0.17.

Рисунок 20. Выдержки из спецификации для настроек GPIO nRF52832


Чтобы настроить какой-либо пин как выход, достаточно записать 1 в бит DIR регистра PIN_CNF[N] (где N – номер пина), а его текущее логическое состояние определяется значением соответствующего бита регистра OUT.

Рисунок 21. Хэдер-файл led для nRF52832 на языках а) С и б) ассемблер



Рисунок 22. Исходный файл led для nRF52832 на языках а) С и б) ассемблер


Таймер nRF52832

Регистры и биты, участвующие в настройке таймера представлены на Рисунке 23.

Рисунок 23. Выдержки из спецификации для настроек таймера nRF52832


Код инициализации таймера должен содержать:
1. Выбор делителя, посредством записи числа от 0 по 9 в регистр PRESCALER. В нашем случае это — 3, что обусловит частоту тактирования таймера в 16 МГц / 2^3 = 2 МГц. Тогда, учитывая, что по дефолту счётчик таймера — 16-разрядный, мы получим период прерываний в 65535 / 2 МГц = 33.8 мс.
2. Запись в регистр CC[0] значения сравнения.
3. Глобальное и локальное (через установку в 1 бита COMPARE0 регистра INTENSET) разрешение прерывания.
4. Включение счётчика таймера записью 1 в задачу TASK_START.

А обработчик прерывания:
• Сброс события EVENTS_COMPARE[0].
• Запуск измерения АЦП.
• Инкремент переменной-счётчика и, по достижению ею заданного значения 15 (иначе, каждые 33.8 мс * 15 = 491 мс), переключение жёлтого светодиода.


Рисунок 24. Хэдер-файл timer для nRF52832 на языках а) С и б) ассемблер


Заметьте, что в хэдер-файле слева присутствует ключевое слово extern, свидетельствующее о вызове внешних функций.

Рисунок 25. Исходный файл timer для nRF52832 на языках а) С и б) ассемблер


Модуль PWM nRF52832

На Рисунке 26 вы можете увидеть регистры и биты, требуемые для настройки модуля PWM.

Рисунок 26. Выдержки из спецификации для настроек модуля PWM nRF52832


Настроим модуль с помощью нижеследующего кода:
1. Запишем номер 18 пина управления зелёным светодиодом в группу битов PIN регистра PSEL.OUT[0]. При этом, бит CONNECT того же регистра должны быть сброшен в 0, что обеспечит соединение модуля PWM с указанным пином.
2. Выберем деление на 4, записав число 2 в регистр PRESCALER, что обусловит частоту тактирования модуля в 16 МГц / 4 = 4 МГц.
3. Учитывая, что ниже мы выберем 14-битную разрядность для АЦП, числом, соответствующим 100%-му рабочему циклу ШИМ-сигнала, будет 2^14 – 1 = 16383, что и занесём в регистр COUNTERTOP.
4. Загрузим в переменную pwmSeq начальное значение рабочего цикла.
5. Укажем адрес pwmSeq, записав его в регистр SEQ[0].PTR.
6. Установим количество циклов равным 1 через регистр SEQ[0].CNT.
7. Разрешим канал PWM0 посредством бита ENABLE одноимённого регистра.
8. Запустим генерацию ШИМ-сигнала через задачу TASKS_SEQSTART[0].


Рисунок 27. Хэдер-файл pwm для nRF52832 на языках а) С и б) ассемблер



Рисунок 28. Исходный файл pwm для nRF52832 на языках а) С и б) ассемблер


Аналого-цифровой преобразователь nRF52832

Выдержки из спецификации с регистрами и их битам, имеющими отношение к настройке и работа АЦП, представлены на Рисунке 29.

Рисунок 29. Выдержки из спецификации для настроек АЦП nRF52832


Функция инициализации преобразователя содержит следующие шаги:
1. Выбрать пин P0.02 (AIN0) в качестве положительного полюса через регистр CH[0].PSELP.
2. Установить 16-битное разрешение, записав число 3 в группу битов VAL регистра RESOLUTION.
3. Записать в группы битов GAIN и REFSEL регистра CH[0].CONFIG значения, обуславливающие выбор VDD в качестве ИОН, а так же приводящие его максимальное значение к разрядности АЦП.
4. Указать адрес переменной-буфера adcValue для хранения результатов измерений, записав его в регистр RESUL.PTR.
5. Установить 1 как максимальное количество измерений за раз, записав это число в регистр RESUL.MAXCNT.
6. Разрешить прерывание АЦП локально посредством регистра INTENSET и глобально.
7. Включить преобразователь записав 1 в бит ENABLE одноимённого регистра.

Функция измерения напряжения должна, запустив задачу TASKS_START, ждать события EVENTS_STARTED, а затем, обнулив последнее, запустить задачу TASKS_SAMPLE.

В обработчике прерывания необходимо:
• Сбросить флаг EVENTS_RESULTDONE.
• Скопировать значение adcValue в переменную pwmSeq из файла pwm.c.
• Перезапустить ШИМ-сигнал.


Рисунок 30. Хэдер-файл adc для nRF52832 на языках а) С и б) ассемблер



Рисунок 31. Исходный файл adc для nRF52832 на языках а) С и б) ассемблер


Обратите внимание на необходимость указания компилятору о привлечении внешней переменной pwmSeq посредством ключевого слова extern.

Файл main.c для nRF52832

Осталось лишь вызвать прописанные нами функции инициализации портов ввода-вывода, таймера, АЦП и модуля PWM в основной функции программы.

Рисунок 32. Файл main для nRF52832 на языках а) С и б) ассемблер


Коды, исходники

🎁atmega8.zip  15.12 Kb ⇣ 12
🎁nrf52832.zip  35.27 Kb ⇣ 7
🎁stm32f401.zip  34.8 Kb ⇣ 9

Спасибо за внимание!

Камрад, рассмотри датагорские рекомендации

🌼 Полезные и проверенные железяки, можно брать

Опробовано в лаборатории редакции или читателями.




 

Читательское голосование

Нравится

Статью одобрили 10 читателей.

Для участия в голосовании зарегистрируйтесь и войдите на сайт с вашими логином и паролем.
 

Поделись с друзьями!

 

 

Связанные материалы

 

Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК.... Сегодня мы рассмотрим работу следующих модулей периферии: • порта ввода-вывода, • таймера •...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 7. Компиляция, отладка, загрузка... Привет датагорцам и гостям нашего кибер-города! В предыдущих частях материала по Ассемблеру...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 6. Протоколы обмена данными I2C и SPI... В проекте из предыдущей части нашей ассемблерной эпопеи мы подключали к микроконтроллеру светодиод...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 3. Макросы и функции... Привет, датагорцы — любители Ассемблера! В пункте 2.5.2 «Инструкции условного перехода» предыдущей...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 4. Система адресации памяти, назначение выводов, тактирование и прерывания МК... Привет датагорцам! Сегодня мы остановимся на следующих вопросах касательно рассматриваемых нами...
Схема на Датагоре. Новая статья Программирование микроконтроллеров на языке C. Часть 2... Добрый день, уважаемые камрады-датагорцы! Сегодня, рассмотрев некоторые общие моменты, мы займёмся...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 2. Шаблонные файлы и инструкции МК... В предыдущей части статьи мы провели подготовительную работу и вкратце разобрали принципы работы...
Схема на Датагоре. Новая статья Электронные часы-термометр с беспроводным датчиком через радиомодуль nRF24L01... Здравствуйте, уважаемые Датагорцы! Представляю вашему вниманию электронные часы с функцией...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 3. TFT дисплей 2.8" (240х320) на ILI9341... Битва за урожай закончена, можно продолжить повествование. Полноцветный TFT-дисплей 240×320 ILI9341...
Схема на Датагоре. Новая статья Программирование микроконтроллеров на языке C. Часть 3... Всем датагорцам привет! Продолжим изучение микроконтроллеров и языка Си. Эффективность программы...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 1. Начало пути... Приветствую всех сограждан и читателей журнала Датагор! Пользуясь кучей времени, предоставленной...
Схема на Датагоре. Новая статья Програмирование в AVR Studio 5 с самого начала. Часть 5... Для того чтобы писать более сложные программы, нужно хорошо представлять структуру и...
 

Комментарии, вопросы, ответы, дополнения, отзывы

 

Добавить комментарий, вопрос, отзыв 💬

Камрады, будьте дружелюбны, соблюдайте правила!

  • Смайлы и люди
    Животные и природа
    Еда и напитки
    Активность
    Путешествия и места
    Предметы
    Символы
    Флаги
 
 
В начало | Зарегистрироваться | Заказать наши киты почтой