Итак, какой-нибудь пример требуется на основе имеющихся уже представлений.
Допустим, мы хотим из контроллера сделать компаратор. Есть вход АЦП и вывод с логическим уровнем 0 - 1.
Если измеренная величина превышает 123, то на ножке будет 1, если не превышает - 0.
Как такое сделать?
У нас есть 2 инструмента: АЦП, и цифрово вывод. Для каждого сделаем свой файл. Сделаем так, чтобы на других платформах потребовалось заменить только их.
Три модуля будет у нас:
1. Модуль АЦП. Инициализация и измерение
2. Модуль GPIO. Настройка и управление выводом.
3. Главный модуль - где и будет алгоритм работы сидеть.
Пример для atmega32. Но подойдёт и для ряда других =)
main.c:
//
// Пример 1: Компаратор
//
// Обратите внимание: здесь нет никаких зависимых от платформы заголовочных файлов а ля avr/io.h, stm32f10x.h, lpc17xx.h и т.д.
// Модуль АЦП
#include "drivers/adc.h"
// Цифровые выводы на порту B
#include "drivers/gpiob.h"
int main(void)
{
// Инициализация АЦП
adc_Init();
// Нулевой вывод на порту B назначить на выход
gpiob_SetOut(1 << 0);
// Бесконечный цикл - прибор работает, пока включён.
while(1)
{
// Измеряем напряжение на нулевом канале АЦП
uint16_t Value = adc_GetValue(0);
// Если величина превышает 123,
if(Value > 123)
{
// то устанавливаем высокий логический уровень;
gpiob_High(1 << 0);
}
else // если же нет,
{
// то устанавливаем низкий логический уровень.
gpiob_Low(1 << 0);
}
}
}
Всё просто и понятно.
drivers/adc.h:
//
// Модуль АЦП.
//
#include <stdint.h>
#ifndef _ADC_H
#define _ADC_H
// Инициализация АЦП
void adc_Init(void);
// Измерение напряжения
uint16_t adc_GetValue(uint8_t Channel);
#endif
Тоже всё просто.
drivers/adc.c:
//
// Модуль АЦП.
//
#include "adc.h"
#include <avr/io.h>
// Инициализация АЦП
void adc_Init(void)
{
// Включаем АЦП
ADCSRA = (1 << ADEN) | // ADC включён
// Предделитель: 16
(1 << ADPS2) | (0 << ADPS1) | (0 << ADPS0);
}
// Измерение напряжения
uint16_t adc_GetValue(uint8_t Channel)
{
// На неверный номер канала получите 0
if (Channel > 7) return 0;
// Ножку на порту A настраиваем на вход:
DDRA &= ~(1 << Channel);
// Выбор канала и опорноо напряжения (с чем сравнивать)
// REFS: 01 (AVCC с внешним конденсатором на выводе AREF)
ADMUX = (0 << REFS1) | (1 << REFS0) |
// Номер канала:
Channel;
// Возможно, здесь нужна небольшая задержка
// ...
// Запуск преобразования
ADCSRA |= (1 << ADSC);
// Цикл крутится, пока бит установлен.
// Он снимается автоматически по завершению преобразования
while (ADCSRA & (1 << ADSC));
// Вернуть результат измерения (регистр ADC двухбайтный)
return ADC;
}
drivers/gpiob.h:
//
// Модуль GPIO
//
#include <stdint.h>
#ifndef _GPIOB_H
#define _GPIOB_H
// Назначить выводы на выход
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние цифровых выходов
void gpiob_SetOut(uint8_t Mask);
// Назначить выводы на вход
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние цифровых входов
void gpiob_SetIn(uint8_t Mask);
// Высокий логический уровень на ножках
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние логической единицы, если порт назначен на выход
void gpiob_High(uint8_t Mask);
// Низкий логический уровень на ножках
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние логического нуля, если порт назначен на выход
void gpiob_Low(uint8_t Mask);
#endif
drivers/gpio.c:
//
// Модуль GPIO
//
#include "gpiob.h"
#include <avr/io.h>
// Назначить выводы на выход
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние цифровых выходов
void gpiob_SetOut(uint8_t Mask)
{
DDRB |= Mask;
}
// Назначить выводы на вход
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние цифровых входов
void gpiob_SetIn(uint8_t Mask)
{
DDRB &= ~Mask;
}
// Высокий логический уровень на ножках
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние логической единицы, если порт назначен на выход
void gpiob_High(uint8_t Mask)
{
PORTB |= Mask;
}
// Низкий логический уровень на ножках
// Mask: маска. Все установленные биты переведут соответствующие ножки
// в состояние логического нуля, если порт назначен на выход
void gpiob_Low(uint8_t Mask)
{
PORTB &= ~Mask;
}
Тут тоже логика достаточно проста. В АРМах практически то же самое, только что регистров побольше и они немного другие.
Архив с примером в AVR Studio 5 тут.