Mootori kiiruse juhtimine

Näide, mis võimaldab nuppudest mootori 0 kiirust suurendada ja vähendada ja pöörlemissuunda muuta. Lisaks on main.c failis ka funktsioon, mis võimaldab kõigi DC nootorite kiiruseid muuta ja suunda vahetada:

void dcmotor_drive(unsigned char index, signed char direction, unsigned char speed)

Hetkel see ei ole mõeldud teegi mootorite osaga kasutamiseks, kuna funktsiooni nimi kattub.

Kõige üldisemalt saab viikude PWM-i juhtida compbuff[CHMAX] muutuja kaudu. Nii võite endale ise funktsioonid jne koostada. Siis saab kõiki SoftPWM.h failis olevaid kanaleid eraldi juhtida.

Kui kanalite arvu muudate, siis vaadake ka SoftPWM.c fail üle, kus tuleb üleliigsete kanalite haldamine kinni keerata: faili lõpus compare funktsioonid.

On oht, et selle SoftPWMi taimer0 kattub hw_delay funktsioonis kasutatuga. Lihtsalt teadmiseks. Kui mujal projektis soovite kasutada, siis kindlasti peab failis, kus on dcmotor_drive funktsioon olema ka volatile unsigned char compbuff[CHMAX];. Vastasel juhul kood ei kompileeru.

Kõik failid allalaadimiseks siin:

/**************************************************************************
*
*
* - File              : main.c
*
*
* - Supported devices : Atmega2561.
*
*
*****************************************************************************/
 
#include <ctype.h>
#include "main.h"
#include <avr/io.h>
#include <homelab/delay.h>
#include <homelab/pin.h>
#include <homelab/module/lcd_gfx.h>
 
 
// Global buffer. Defined SoftPWM.c
volatile unsigned char compbuff[CHMAX];
 
pin led_red    = PIN(C, 5);
pin led_yellow = PIN(C, 4);
pin led_green  = PIN(C, 3);
 
pin button1 = PIN(C, 0);
pin button2 = PIN(C, 1);
pin button3 = PIN(C, 2);
 
 
//
// The program entry point.
//
int main(void)
{
  unsigned char rxdata = 0;
  signed char direction = -1;
 
 
    // Seab LEDid töökorda
	pin_setup_output(led_red);
	pin_setup_output(led_yellow);
	pin_setup_output(led_green);
 
	// Seab nupud töökorda
	pin_setup_input_with_pullup(button1);
	pin_setup_input_with_pullup(button2);
	pin_setup_input_with_pullup(button3);
 
	// Lülitab LEDid välja
	pin_set(led_green);
	pin_set(led_yellow);
	pin_set(led_red);
 
	// Ekraani seadistamine
	lcd_gfx_init();
 
	// LCD ekraani puhastamine
	lcd_gfx_clear();
 
	lcd_gfx_backlight(true); 
 
	// Kursori (nähtamatu) ekraani keskele viimine
	lcd_gfx_goto_char_xy(3, 2);
 
	// Teksti kuvamine
	lcd_gfx_write_string("SoftPWM");
 
 
  	InitSoftPWM();
 
 
  	while(1)
  	{
 
 
		// Nupp S1 alla vajutatud
		if(!pin_get_debounced_value(button1))
		{
			// Kiiruse suurendamine
			if(rxdata < 0xFF)
			{
				rxdata++;
			}
			pin_clear(led_green);
 
		}
 
		// Nupp S2 alla vajutatud
	    if(!pin_get_debounced_value(button2))
		{
			// Kiiruse vähendamine
			if(rxdata > 0x00)
			{
				rxdata--;
			}
			pin_clear(led_yellow);
 
		}
 
		// Nupp S3 alla vajutatud
		if(!pin_get_debounced_value(button3))
		{
			//Pöörlemissuuna muutmine
			if(direction == 1)
			{
				direction = -1;
			}
			else
			{
				direction = 1;		
			}
 
		}
 
		pin_set(led_green);
		pin_set(led_yellow);		
 
		//Mootori juhtimine
		dcmotor_drive(0, direction, rxdata);
		sw_delay_ms(5);
 
		/*
		dcmotor_drive(0, 1, 0xFF);
		sw_delay_ms(500);
		dcmotor_drive(0, -1, 0xFF);
		sw_delay_ms(500);
		*/
 
  	}
}
 
//
// Funktsioon mootori juhtimiseks.
//
void dcmotor_drive(unsigned char index, signed char direction, unsigned char speed) 
{
	switch (index)
	{
		case 0:
			if(direction == 1)
			{
				compbuff[6] = 0x00;
				compbuff[7] = speed;
			}
			if(direction == -1)
			{
				compbuff[7] = 0x00;
				compbuff[6] = speed;				
			}
 
			break;
 
 
		case 1:  //OK
 
			if(direction == 1)
			{
				compbuff[0] = 0x00;
				compbuff[1] = speed;
			}
			if(direction == -1)
			{
				compbuff[0] = speed;
				compbuff[1] = 0x00;
			}
 
			break;
 
		case 2: //OK
 
			if(direction == 1)
			{
				compbuff[4] = 0x00;
				compbuff[5] = speed;
			}
			if(direction == -1)
			{
				compbuff[5] = 0x00;
				compbuff[4] = speed;				
			}
 
			break;
 
		case 3: //OK
 
			if(direction == 1)
			{
				compbuff[2] = 0x00;
				compbuff[3] = speed;
			}
			if(direction == -1)
			{
				compbuff[3] = 0x00;
				compbuff[2] = speed;
			}
 
			break;
 
		//default:
 
 
	}
}

PWM generaator:

//*****************************************************************************
//
// 10 Channel software PWM
//
//  - AppNote           : AVR136 - Low-Jitter Multi-Channel Software PWM
//
// Introduction
// This documents data structures, functions, variables, defines, enums, and
// typedefs in the software for application note AVR136.
//
//
// Channels are mapped to specific port pins in the SoftPWM.h file
// \section PWMinfo PWM frequency and crystal selection
//
// The PWM base frequency is the crystal frequency divided by 65536, e.g. for
// a 7.3728MHz crystal the PWM base frequency will be 112.5Hz. The standard
// STK500 3.6864MHz oscillator could be used as a clock source, but the PWM
// base frequency would be reduced which may result in unacceptable ripple.
// Jitter will be +/-1 clock cycle max, or +/-0.0015% of base frequency.
//
// This demonstration shows ten PWM channels, for GCC the ISR uses less than
// 50% of processing time during the softcount=0 interrupt. The principles
// shown should accomodate up to 24 channels on suitable AVR devices whilst
// maintaining PWM accuracy, ISR optimisation may improve this even further.  
//*****************************************************************************
 
#include "SoftPWM.h"
 
// global buffers
unsigned char compare[CHMAX];
extern volatile unsigned char compbuff[CHMAX];
 
//
// Init function. This function initialises the hardware
//
void InitSoftPWM(void)
{
  unsigned char i, pwm;
 
  CLKPR = (1 << CLKPCE);        // enable clock prescaler update
  CLKPR = 0;                    // set clock to maximum (= crystal)
 
  __watchdog_reset();           // reset watchdog timer
  MCUSR &= ~(1 << WDRF);        // clear the watchdog reset flag
  WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence
  WDTCSR = 0x00;                // disable watchdog timer
 
  DDRD = PORTD_MASK;            // set port pins to output
  DDRB = PORTB_MASK;            // set port pins to output
 
 
  pwm = PWMDEFAULT;
 
 
  for(i=0 ; i<CHMAX ; i++)      // initialise all channels
  {
    compare[i] = pwm;           // set default PWM values
    compbuff[i] = pwm;          // set default PWM values
  }
 
 
  TIFR0 = (1 << TOV0);           // clear interrupt flag
  TIMSK0 = (1 << TOIE0);         // enable overflow interrupt
  TCCR0B = (1 << CS00);         // start timer, no prescale
 
  sei();         // enable interrupts
}
 
//
// Interrupt Service Routine
//
ISR (TIMER0_OVF_vect)
{
  static unsigned char pinlevelB=PORTB_MASK, pinlevelD=PORTD_MASK;
  static unsigned char softcount=0xFF;
 
  PORTB = pinlevelB;            // update outputs
  PORTD = pinlevelD;            // update outputs
 
 
  if(++softcount == 0){         // increment modulo 256 counter and update
                                // the compare values only when counter = 0.
    compare[0] = compbuff[0];   // verbose code for speed
    compare[1] = compbuff[1];
    compare[2] = compbuff[2];
    compare[3] = compbuff[3];
    compare[4] = compbuff[4];
    compare[5] = compbuff[5];
    compare[6] = compbuff[6];
    compare[7] = compbuff[7];
	//compare[8] = compbuff[8];
	//compare[9] = compbuff[9];
    // last element should equal CHMAX - 1
 
    pinlevelB = PORTB_MASK;     // set all port pins high
    pinlevelD = PORTD_MASK;     // set all port pins high
  }
  // clear port pin on compare match (executed on next interrupt)
  if(compare[0] == softcount) CH0_CLEAR;
  if(compare[1] == softcount) CH1_CLEAR;
  if(compare[2] == softcount) CH2_CLEAR;
  if(compare[3] == softcount) CH3_CLEAR;
  if(compare[4] == softcount) CH4_CLEAR;
 
  if(compare[5] == softcount) CH5_CLEAR;
  if(compare[6] == softcount) CH6_CLEAR;
  if(compare[7] == softcount) CH7_CLEAR; 
  //if(compare[8] == softcount) CH8_CLEAR;
  //if(compare[9] == softcount) CH9_CLEAR;   
 
 
}