Оригинал статьи здесь https://radioparty.ru/programming/avr/c ... all-sensor
Изменил источник опорного напряжения на внутренний (1,1V), лучше подходит для измерения тока, поэтому потенциометр подключил через делитель напряжения. Так как в дальнейшем планируется измерение тока каналы измерения меняются в обработчике прерывания от АЦП, а само прерывание происходит от переполнения таймера. Переменная start_speed содержит значение при котором начинается запуск двигателя.
Реверс двигателя осуществляется переключателем S1 при полной остановке двигателя.
Выходы на светодиоды "Реверс" и "Макс. ток" в коде пока не использовал, а также переменную current.
Код: Выделить всё
#include <avr/io.h>
#include <avr/interrupt.h>
// Фаза U(Верхнее плечо)
#define UH_ON TCCR0A |= (1 << COM0A1);
#define UH_OFF TCCR0A &= ~(1 << COM0A1);
// Фаза U(Нижнее плечо)
#define UL_ON PORTD |= (1 << PD7);
#define UL_OFF PORTD &= ~(1 << PD7);
// Фаза V(Верхнее плечо)
#define VH_ON TCCR0A |= (1 << COM0B1);
#define VH_OFF TCCR0A &= ~(1 << COM0B1);
// Фаза V(Нижнее плечо)
#define VL_ON PORTD |= (1 << PD4);
#define VL_OFF PORTD &= ~(1 << PD4);
// Фаза W(Верхнее плечо)
#define WH_ON TCCR2A |= (1 << COM2B1);
#define WH_OFF TCCR2A &= ~(1 << COM2B1);
// Фаза W(Нижнее плечо)
#define WL_ON PORTD |= (1 << PD2);
#define WL_OFF PORTD &= ~(1 << PD2);
#define PHASE_ALL_OFF UH_OFF;UL_OFF;VH_OFF;VL_OFF;WH_OFF;WL_OFF;
#define start_speed 2
char start;
volatile uint16_t current;
volatile uint8_t speed;
volatile char direction, channel = 3;
// Обработчик внешнего прерывания
ISR(PCINT0_vect)
{
uint8_t position;
// Считываем значения датчиков
position = (PINB & ((1 << PB2)|(1 << PB1)|(1 << PB0)));
if(direction) // Если по часовой
{
switch(position) // Переключаем обмотки
{
case 0b101: UH_OFF; UL_ON; VH_ON; VL_OFF; WH_OFF; WL_OFF; break;
case 0b001: UH_OFF; UL_ON; VH_OFF; VL_OFF; WH_ON; WL_OFF; break;
case 0b011: UH_OFF; UL_OFF; VH_OFF; VL_ON; WH_ON; WL_OFF; break;
case 0b010: UH_ON; UL_OFF; VH_OFF; VL_ON; WH_OFF; WL_OFF; break;
case 0b110: UH_ON; UL_OFF; VH_OFF; VL_OFF; WH_OFF; WL_ON; break;
case 0b100: UH_OFF; UL_OFF; VH_ON; VL_OFF; WH_OFF; WL_ON; break;
}
}
else // иначе против часовой
{
switch(position) // Переключаем обмотки
{
case 0b101: UH_ON; UL_OFF; VH_OFF; VL_ON; WH_OFF; WL_OFF; break;
case 0b001: UH_ON; UL_OFF; VH_OFF; VL_OFF; WH_OFF; WL_ON; break;
case 0b011: UH_OFF; UL_OFF; VH_ON; VL_OFF; WH_OFF; WL_ON; break;
case 0b010: UH_OFF; UL_ON; VH_ON; VL_OFF; WH_OFF; WL_OFF; break;
case 0b110: UH_OFF; UL_ON; VH_OFF; VL_OFF; WH_ON; WL_OFF; break;
case 0b100: UH_OFF; UL_OFF; VH_OFF; VL_ON; WH_ON; WL_OFF; break;
}
}
}
ISR(ADC_vect)
{
switch(channel)
{
case 3:
ADMUX = (1 << REFS1)|(1 << REFS0)|(1 << MUX2);
speed = ADC/4;
channel = 4;
break;
case 4:
ADMUX = (1 << REFS1)|(1 << REFS0)|(1 << MUX1)|(1 << MUX0);
current = ADC;
channel = 3;
break;
}
TIFR0 = (1 << TOV0);
}
// Функция изменения скорости
static void set_speed(uint8_t speed)
{
TIFR2 = TIFR2; // Очищаем флаг прерывания T0
while(!(TIFR2 & (1<<TOV2))); // Ждем переполнения таймера
cli();
OCR0A = speed; // Изменяем ШИМ
OCR0B = speed;
OCR2B = speed;
sei();
}
int main(void)
{
TCCR0A |= (1 << WGM01)|(1 << WGM00); // Режим Fast PWM
TCCR0B |= (1 << CS00); // Предделитель CLK/1
TCCR2A |= (1 << WGM01)|(1 << WGM00); // Режим Fast PWM
TCCR2B |= (1 << CS20); // Предделитель CLK/1
PCMSK0 |= (1 << PCINT2)|(1 << PCINT1)|(1 << PCINT0); // Активируем входы внешних прерываний
PCICR |= (1 << PCIE0); // Разрешаем прерывание по изменению состояния порта B
ADMUX = (1 << REFS1)|(1 << REFS0) // Внутренний ИОН 1,1V
|(1 << MUX1)|(1 << MUX0); // Вход ADC3
ADCSRA = (1 << ADEN) // Разрешаем ADC
|(1 << ADSC) // Запускаем преобразование
|(1 << ADATE) // Непрерывный режим преобразования
|(1 << ADIF) // Очищаем флаг прерывания
|(1 << ADIE) // Разрешаем прерывание ADC
|(1 << ADPS1); // Предделитель 4
ADCSRB = (1 << ADTS2); // Запуск преобразования по переполнению Т0
DDRC &= ~(1 << PC5); // Переключатель направления вращения
PORTC |= (1 << PC5); // Вкл. внутренний подтягивающий резистор
sei(); // Глобально разрешаем прерывания
DDRD = 0xFF; // Порт D - выход
while(1)
{
if(speed > start_speed) // Если значение скорости больше скорости запуска
{
// Режим запуска
if(start) // Если пуск разрешен, запускаем двигатель
{
if(PINC&(1 << PC5)) direction = 1; // По часовой
if((PINC&(1 << PC5)) == 0) direction = 0; // Против часовой
PCICR |= (1 << PCIE0); // Разрешаем прерывание по изменению состояния порта B
DDRB |= (1 << PB2)|(1 << PB1)|(1 << PB0); // Кратковременно подаем лог.1 на входы датчиков
PORTB |= (1 << PB2)|(1 << PB1)|(1 << PB0); // для первоначального запуска двигателя
PORTB &= ~(1 << PB2)|(1 << PB1)|(1 << PB0);
DDRB = 0x00; // Порт B - вход
start = 0; // Запрещаем запуск
}
// Рабочий режим
set_speed(speed); // Изменяем скорость
}
else
{
start = 1; // Разрешаем запуск
speed = 0;
PCICR &= ~(1 << PCIE0); // Запрещаем прерывание по изменению состояния порта B
PHASE_ALL_OFF; // Выключаем выхода на двигатель
}
}
}