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; }