Управление скоростью ведётся по ПИД-закону. Информация о текущей скорости передаётся по RS232, по нему же планирую передавать задание.
Всё работает исправно, поддерживает скорость, достаточно быстро выходит на уставку. Проблема в том, что контроллер успевает посчитатьь только 30 импульсов за 0,25 секунды (120 имп за секунду). 4 раза в секунду текущая скорость сравнивается с заданием и вносится поправка в выходной сигнал. Частота работы датчика 600 Гц, частота оптрона (стоит между датчиком и входом прерывания)- килогерцы , физически обратная связь отработать успевает.
Гляньте мой код, может свежим взглядом, кто увидит возможную причину запаздывания.
- Код: Выделить всё • Развернуть
#include <mega16.h>
//#include <m8_128.h> // http://avr123.nm.ru/m8_128.h
#include <delay.h>
#include <stdio.h>
#include <io.h>
int pwmPD4, pwmPD5, s, sumint;
int count, time, setpoint, n;
float E, E_pred, Y, Sum, XP, Tau_I, Tau_D, D, I;
unsigned char delta ;
int pwm = 0; // Величина ШИМ начальная PWM в единицах от 1 до 1023
interrupt [TIM0_OVF] void TIM0_OVF_isr(void) //обработа прерывания от таймера
{
//TCNT1H = 0x00; // обнулить счет таймера
//TCNT1L = 0x00;
PORTC.0=0; // включаем реле к2
PORTC.1=1;
PORTC.6=1; //включаем средний сегмент
PORTA.0^=1; //инвертируем выход порта
TCNT0=s; // таймер считает от этого значения до 255
SREG=1; //разрешение прерываний
GIFR=0x01 ; //сброс флага переполнения таймера --- 0
time++; //инкремент количества переполнений таймера
if (time>=10) // 40 переполнений = 1 секунде
{
printf("imp %u %c\r\n",count,'');
// printf("TIME %u %c\r\n",time,'--');
printf("pwm %u %c\r\n",pwmPD5,'');
printf("sum %u %c\r\n",sumint,'');
PORTA.1^=1; // мигаем каждый такт
// Т А У //
XP=2; // коэффициент пропорциональности
Tau_I=0.01; //постоянная интегрирования
Tau_D=2; //постоянная дифференцирования
E=setpoint-count; //вычисляем текущее рассогласование (разницу между заданием и текужим значением)
Sum=Sum+E;
if (Sum<0) {Sum=2;} //увеличиваем сумму ошибок
D=Tau_D*(E-E_pred)/0.25; //звено дифференцирования
I=(Sum*0.25)/Tau_I; //звено интегрирования
Y=(1/XP)*(E+D+I) ; //итоговая формула
if (Y<1) {Y=1;} ;
if (Y>=pwm) { PORTC.2=1; PORTC.3=0; } else
if (Y<pwm) { PORTC.2=0; PORTC.3=1; }
pwm=Y;
sumint=Sum;
E_pred=E;
// if (count<setpoint) {pwm=pwm+10*(setpoint-count); PORTC.2=0; PORTC.3=1; } else
// if (count>setpoint) {pwm=pwm-10*(count-setpoint); PORTC.2=1; PORTC.3=0;}
count=0; //обнулить количество импульсов от прерывания
time=0; // обнулить кол-во прерываний от таймера
}
}
interrupt [EXT_INT0] void ext_int0_isr(void) // обработка прерывания int0 при срабатывании датчика по возрастающему фронту
{
PORTA.5^=1;
count++;
n++; //увеличение кол-ва импульсов-прерываний
if (n>5) { PORTC.7=1; }
/*pwmPD4+=20; //увеличение широты импульса для ШИМ сигнала на ножке PD4
if (pwmPD4 > 1025) //если ШИМ уже более 100 %
{ pwmPD4 = 0;}; // обнулить величину ШИМ */
SREG=1;
}
void main(void)
{
count=0;
time=0;
setpoint=15; //задание скорости ////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Declare your local variables here
// Input/Output Ports initialization
PORTA=0xFF;
DDRA=0xFF;
PORTB=0x00;
DDRB=0x00;
PORTC=0x00;
DDRC=0xFF;
// Port D initialization
PORTD=0xFF;
DDRD=0x30; // 0011 0000 PD5(OC1A) PD4(OC1B) - PWM Timer1 OUT
//Настройка таймера 0 таймер считает с периодом 24,96 мс за 40 переполнений получится 1 сек
s=0x3D; // таймер считает от этого (61) значения до 255 то есть 194 такта !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
TCCR0=0x05; //частота таймера в 1024 раз меньше тактовой
TCNT0=s; // таймер считает от этого значения до 255
OCR0=0x00;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSRA=0x00;
UCSRB=0x08;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x33;;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Fast PWM top=03FFh
// OC1A output: Non-Inv.
// OC1B output: Non-Inv.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0b10100011;
TCCR1B=0b00001011; // делитель 64
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
// +++++++++++++++++++++++++++++++++++++++++++++++
// установить величину ШИМ на PD5 === 0
OCR1AH=0x00;
OCR1AL=0x00;
// +++++++++++++++++++++++++++++++++++++++++++++++
// установить величину ШИМ на PD4 === 0
OCR1BH=0x00;
OCR1BL=0x00;
// External Interrupt(s) initialization
// INT0: On
// INT1: Off
// INT2: Off
GICR=0x40; //включено прерывание Int0
MCUCR=0x03; //по нарастающему фронту на ножке PD2
MCUCSR=0x00;
GIFR=0x00;
TIMSK=0x01; //прерывание при переполнении таймера 0
ACSR=0x80;
SFIOR=0x00;
// Global enable interrupts
#asm("sei") ;
// +++++++++++++++++++++++++++++++++++++++++++++++
// установить ШИМ 25% на PD4
//OCR1BH = 0x00; // PWM(PD4) OCR1B / 10.23 (%)
//OCR1BL = 0xFF; // PWM(PD4) 255 / 10.23 = 24.9 (%)
// +++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++
// установить ШИМ 25% на PD5
//OCR1AH = 0x00; // PWM(PD5) OCR1A / 10.23 (%)
//OCR1AL = 0xFF; // PWM(PD5) 255 / 10.23 = 24.9 (%)
// +++++++++++++++++++++++++++++++++++++++++++++++
while (1){
// Place your code here
if (pwm > 1025) { //если ШИМ уже более 100 %
pwm = 1022; // обнулить величину ШИМ
};
////////////////////////////////////////новое значение ШИМ сигнала
pwmPD5=pwm;
/* printf("PWM %u %c\n",pwm,'%');
// вывели новое значение ШИМ в % */
// +++++++++++++++++++++++++++++++++++++
// pwm_val - это число от 0 до 1023
// PWM(PD5) = OCR1A / 10.23 (%)
OCR1AH = (char)(pwmPD5>>8);
OCR1AL = (char)pwmPD5;
//OCR1BH = (char)(pwmPD4>>8);
//OCR1BL = (char)pwmPD4;
// pwm += 10; //увеличим ШИМ на 10%
delay_ms(1); // пауза
}; // закрывающая скобка для while(1)
}