logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega32][C] Problem przy odczycie małej klawiatury + PWM

sebke 16 Maj 2009 00:23 1403 3
  • #1 6535483
    sebke
    Poziom 10  
    Witam użytkowników forum.


    Korzystając z zestawu zl3avr (z uC Atmega32) chciałem sterować pracą silnika krokowego (unipolarnego). Jako że jestem poczatkującym w tej dziedzinie to efekt jest nie za dobry. Na chwile obecną jestem w stanie sterować pracą silniczka w prawo i w lewo, ale żeby to było możliwe to musze mieć cały czas wciśnięty odpowiedni przycisk. Kierunek obrotu silnika jest wyświetlany na LCD(2x16).

    Programowi jest daleko do doskonałości ponieważ nie bardzo wiem jak zaimplementować PWM aby móc płynnie sterować mocą silniczka, a kolejnym problemem jest dodanie kolejnych przycisków. Obecnie mogę korzystać tylko z dwóch, próba dodania kolejnego przycisku nie przynosi porządanego efektu.

    Ponieważ po przewertowaniu juz kilkudziesięciu stron nie znalazłem rozwiązania mojego problemu. Stwierdziłem, ze któryś z użytkowników forum moze znać rozwiązanie tych trudności i naprowadzi mnie jak mogę się z tym uporać. Oczywiście ogólną zasadę działania PWM znam tylko nie bardzo wiem jak to napisać w C aby zadziałało na Atmega32.

    Na początku kodu jest zamieszczony kod do LCD dopiero pod koniec jest właściwy nie do końca działający program :/



    /* Program demonstrujacy dzialanie silnika krokowego
    
    Piny/Pins:
    PD1 - RS
    PD2 - E
    PD3 - D4
    PD4 - D5
    PD5 - D6
    PD6 - D7
    */
    
    #define F_CPU 8000000UL 
    #include <avr/io.h>
    #include <stdlib.h>
    #include <util/delay.h>
    
    #define DDR_DB4 DDRD
    #define PORT_DB4 PORTD
    #define DB4 PD3
    
    #define DDR_DB5 DDRD
    #define PORT_DB5 PORTD
    #define DB5 PD4
    
    #define DDR_DB6 DDRD
    #define PORT_DB6 PORTD
    #define DB6 PD5
    
    #define DDR_DB7 DDRD
    #define PORT_DB7 PORTD
    #define DB7 PD6
    
    
    #define DDR_RS DDRD
    #define PORT_RS PORTD
    #define RS PD1
    
    #define DDR_E DDRD
    #define PORT_E PORTD
    #define E PD2
    
    
    #define SET_B1 PORT_B1 |= _BV(B1)
    #define CLR_B1 PORT_B1 &= ~_BV(B1)
    
    #define SET_B2 PORT_B2 |= _BV(B2)
    #define CLR_B2 PORT_B2 &= ~_BV(B2)
    
    #define SET_B3 PORT_B3 |= _BV(B3)
    #define CLR_B3 PORT_B3 &= ~_BV(B3)
    
    #define SET_B4 PORT_B4 |= _BV(B4)
    #define CLR_B4 PORT_B4 &= ~_BV(B4)
    
    #define SET_DB4 PORT_DB4 |= _BV(DB4)
    #define CLR_DB4 PORT_DB4 &= ~_BV(DB4)
    
    #define SET_DB5 PORT_DB5 |= _BV(DB5)
    #define CLR_DB5 PORT_DB5 &= ~_BV(DB5)
    
    #define SET_DB6 PORT_DB6 |= _BV(DB6)
    #define CLR_DB6 PORT_DB6 &= ~_BV(DB6)
    
    #define SET_DB7 PORT_DB7 |= _BV(DB7)
    #define CLR_DB7 PORT_DB7 &= ~_BV(DB7)
    
    #define SET_E PORT_E |= _BV(E)
    #define CLR_E PORT_E &= ~_BV(E)
    
    #define SET_RS PORT_RS |= _BV(RS)
    #define CLR_RS PORT_RS &= ~_BV(RS)
    
    #define LCD_X 16
    #define LCD_Y 2
    
    
    union 
    	{
    	int tds;
    	char nds[2];
    	}	ds;
    
    char buf[6]; 
    
    void out_nibble(char x)
    	{
    	CLR_DB4;
    	CLR_DB5;
    	CLR_DB6;
    	CLR_DB7;
    	if(x & _BV(0)) SET_DB4;
    	if(x & _BV(1)) SET_DB5;
    	if(x & _BV(2)) SET_DB6;
    	if(x & _BV(3)) SET_DB7;
    	}
    
    
    void write_to_lcd(char x)
    	{
    	SET_E;
    	out_nibble(x >> 4);
    	CLR_E;
    	SET_E;
    	out_nibble(x);
    	CLR_E;
    	_delay_ms(1);
    	
    	
    	}
    
    void delay(int useconds)
    	{	
    	int s;
    	useconds = useconds;
    	for (s=0; s<useconds;s++);
    	}
    
    void write_command(char x)
    	{
    	CLR_RS;
    	write_to_lcd(x);
    	}
    
    void write_char(char x)
    	{
    	SET_RS;
    	write_to_lcd(x);
    	}
    
    void write_text(char * s)
    	{
    	while(*s)
    		{
    		write_char(*s);
    		s++;
    		}
    	}
    
    void lcd_init(void)
    	{
    	DDR_DB4 |= _BV(DB4);
    	DDR_DB5 |= _BV(DB5);
    	DDR_DB6 |= _BV(DB6);
    	DDR_DB7 |= _BV(DB7);
    	DDR_E |= _BV(E);
    	DDR_RS |= _BV(RS);
    
    	
    	_delay_ms(15);
    	CLR_E;
    	CLR_RS;
    	char i;
    	for(i = 0; i < 3; i++)
    		{
    		SET_E;
    		out_nibble(0x03);
    		CLR_E;
    		_delay_ms(5);
    
    		}
    	SET_E;
    	out_nibble(0x02);
    	CLR_E;
    	_delay_ms(1);
    	write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    	write_command(0x08); // wyłącz LCD, kursor i miganie
    	write_command(0x01); // czyść LCD
    	write_command(0x06); // bez przesuwania w prawo
    	write_command(0x0C); // włącz LCD, bez kursora i mrugania
    	}
    
    
    void LCD_xy(uint8_t x, uint8_t y)
    	{
    	switch(y)
    		{
    		case 1: y=0x40; break;
    		case 2: y=0x14; break;
    		}
    	write_command(0x80+y+x);
    	}
    
    void lcdxy(uint8_t x, uint8_t y)
    	{
    	switch(y)
    		{
    		case 1: y=0x40; break;
    		case 2: y=0x14; break;
    		}
    	write_command(0x80+y+x);
    	}
    void LCD_clr(void)
    	{
    	write_command(0x01);
    	_delay_ms(1);
    	
    	LCD_xy(0,0);
    	}
    
    
    //==========================================================================================//
    int main ()
    {	
    	
    	lcd_init();
    
    	while(1)
    	{
    		LCD_xy(1,0);
     	write_text("Obrot silnika w:");
    			
    	/* Wszystkie linie portu B będą wyjściami*/ 
      		DDRB  = 0xff;
    
     	// linie PC0,PC1 będą wejściami z podciągnięciem do VCC 
      	DDRC = 0x00;
      	PORTC = 0x03;
    		//     Jeśli pierwszy przycisk wciśnięty 
    	
    		if(!(PINC & 1<<0)) 
    		{
    			LCD_xy(0,1);
    			write_text("lewo");
    		
    		
    				for(int k=3;k>=0;k--)
    				{
    			
    				PORTB = _BV(k);
    				_delay_ms(50);
    				
    				}
    		 	
    
    	 		LCD_clr();
      		}
    		else 
    			{
    				PORTB = 0x00;
    			}
    	
    	//Jeśli drugi przycisk wciśnięty 
        
    	if(!(PINC & 1<<1))
    	{
    		LCD_xy(0,1);
    		write_text("prawo");
    			
    			
    				for(int k=0;k<=3;k++)
    				{
    		
    				PORTB = _BV(k);
    				_delay_ms(100);
    				}
    			
    	
    		LCD_clr();
    	} 	
    		else{PORTB = 0x00;} 
    	
    	if(!(PINC & 1<<2))
    	{
    		LCD_xy(0,10);
    		write_text("prawda");
    	}
    			
    }
    		
    	
    }
  • Pomocny post
    #2 6535653
    Dr.Vee
    VIP Zasłużony dla elektroda
    PWM mógłbyś użyć do sterowania silnika prądu stałego, ale nie silnika krokowego.

    W Twoim przypadku żeby zmienić prędkość musiałbyś zmienić opóźnienie pomiędzy impulsami podawanymi na uzwojenia silnika. Na początek napisz coś takiego:
    uint8_t predkosc = 0;
    for (int i = 0; i < 4; ++i) {
        PORTB = _BV(k);
        switch (predkosc) {
            case 0:
                _delay_ms(50);
                break;
            case 1:
                _delay_ms(40);
                break;
            case 2:
                _delay_ms(30);
                break;
            case 3:
            default:
                _delay_ms(20);
                break;
        }
    }
    W ten sposób zmieniając zmienna predkosc od 0 do 3 będziesz miał 4
    różne prędkości obrotów.

    Żeby uzyskać płynną zmianę prędkości będziesz musiał do generacji impulsów
    użyć układu timera. Zmieniając czas pomiędzy wystąpieniami przerwania od
    timera zmienisz prędkość obrotową silnika.

    Co do testowania przycisków to wyrażenie
    (PINC & 1 << 2) === ((PINC & 1) << 2)
    Nie do końca o to Ci chodzi :)
    Najlepiej pisz z użyciem makra _BV(x):
    if (!(PINC & _BV(2)))
        /* wcisnieto przycisk podlaczony pod PC2 */


    Pozdrawiam,
    Dr.Vee
  • #3 6539268
    sebke
    Poziom 10  
    Dzięki za podpowiedz Dr.Vee

    Twoja sugestia okazała sie bardzo przydatna. Teraz tylko musze wykombinować jak aby rozrzeszyć to na pełną klawiaturę matrycową(4x4).
    Cytat:
    Żeby uzyskać płynną zmianę prędkości będziesz musiał do generacji impulsów użyć układu timera. Zmieniając czas pomiędzy wystąpieniami przerwania od timera zmienisz prędkość obrotową silnika.

    Nie bardzo wiem jak to wykonać :/

    Oprócz tego co było wcześniej dołożyłem:

    void w_prawo(uint8_t predkosc) 
    {
    	for (int i=0; i<4; i++) 
    	{ 
        PORTB = _BV(i); 
        	switch (predkosc)
    	 	{ 
            	case 0: _delay_ms(100); break; 
            	
    			case 1: _delay_ms(80); break; 
            
    			case 2: _delay_ms(60); break; 
            	
    			case 3:	default: _delay_ms(20); break; 
        	} 
    	}
    }
    
    void w_lewo(uint8_t predkosc)
    {
    
    	for (int i=3; i>=0; i--) 
    	{ 
        PORTB = _BV(i); 
        	switch (predkosc)
    	 	{ 
            	case 0: _delay_ms(100); break; 
            	
    	case 1: _delay_ms(80); break; 
            
    	case 2: _delay_ms(60); break; 
            	
    	case 3:	default: _delay_ms(20); break; 
        	}
    }
    	
    }
    


    I teraz main wyglada tak:

    int main ()
    {
    	lcd_init();
    	
    	DDRB = 0xff;	//Wszystkie linie portu B będą wyjściami
    	DDRA = 0x00;	// linie PA0,PA1 będą wejściami z podciągnięciem do VCC 
     	 	
      	PORTA = 0x0f;
    
    	while(1);
    	{
    		LCD_xy(1,0);
     		write_text("Obrot silnika w:");	
    	
    	if (!(PINA &_BV(0))) //     Jeśli pierwszy przycisk wciśnięty 
    		{
    			LCD_xy(0,1);
    			write_text("lewo");
    		
    			w_lewo(4);
    			LCD_clr();
    	 	}
    			
    		//Jeśli drugi przycisk wciśnięty 
        
    		if (!(PINA & _BV(1)))
    		{
    		LCD_xy(0,1);
    		write_text("prawo");
    			
    		w_prawo(1);
    		LCD_clr();	
    		} 	
    		else{PORTB = 0x00;} 
    		
    		if (!(PINA & _BV(2)))
    		{
    		LCD_xy(0,1);
    		write_text("prawo");
    			
    		w_prawo(4);
    		LCD_clr();	
    			
    		} 	
    		else{PORTB = 0x00;} 
    	}
    }


    Tylko nadal mam problem ponieważ aby silnik się obracał musze trzymać przycisk.

    Może ktoś mam jakąś sugestie jak sobie z tym poradzić ? Rozwiązanie z dołozeniem kolejnej petli while() po naciśnięciu przycisku nie jest dobrym rozwiązaniem jak by któś miał taki pomysł :)
  • #4 6542371
    Dr.Vee
    VIP Zasłużony dla elektroda
    Dodajesz zmienną:
    /* definicja typu "kierunek_t" i jego wartości */
    typedef kierunek_t enum { DIR_STOP, DIR_PRAWO, DIR_LEWO };
    
    /* zmienne w main */
    kierunek_t poprzedni_kierunek;
    kierunek_t kierunek = DIR_STOP;
    
    while (1) {
        poprzedni_kierunek = kierunek;
    
        if (/* warunek dla kierunku w prawo */) {
            kierunek = DIR_PRAWO;
        } else if (/* warunek dla kierunku w lewo */) {
            kierunek = DIR_LEWO;
        } else if (/* warunek dla zatrzymania */) {
            kierunek = DIR_STOP;
        }
    
        if (kierunek != poprzedni_kierunek) {
            /* jeśli kierunek się zmienił, wypisz na LCD */
        }
    
        switch (kierunek) {
            case DIR_STOP:
                /* zatrzymaj */
                break;
            case DIR_PRAWO:
                /* obracaj w prawo */
                break;
            case DIR_LEWO:
                /* obracaj w lewo */
                break;
        }
    }

    Pozdrawiam,
    Dr.Vee
REKLAMA