projekt - navrh riadenia pre roboticke rameno

Ahojte
pracujem na DP na navrhu robotickeho ramena.

V minulosti ste mi uz radili na mojom starsom projekte o digitalnom kompase:
[forum.mcontrollers.com/t/projekt-elektronicky-kompas-s-vystupom-na-displej/641/1)

Zopar informacii:
vypocet kinematiky bude prebiehat na pc
hodnoty pre serva budu prenasane cez usart
no a rozhranie bude generovat signaly pre serva

rozhodol som sa pre atmega8, s krystalom 16 Mhz
mam zvladnutu komunikaciu cez usart aj na strane PC aj na strane mikrokontrolera (aspon si to myslim) - rychlost 38400 baud

ide mi o to, aby bol vysledny pohyb plynuly a bez trhania

co zatial viem, co som studoval na zahranicnych strankach, bude to mat co do cinenia s timermi a preruseniami - rozdelit 20ms okno na viacero casti, pre kazde servo jednu

moja otazka znie: je vobec mozne generovat za pomoci spominaneho mcu pwm pre 6 servomotorov s dostatocnou presnostou? (aspon 9 - 10 bitov)

ako na to?

Pro 6ch pwm nemá mega8 podporu, bude to třeba spáchat programově pomocí timeru.
V jiných tématech by ses dočet, že perioda pro servo je 20ms (50Hz) a regulační rozsah je asi 1ms (1ms - 0%, 2ms - 100%). Bude-li ti stačit 10b na celou periodu (nikoli jen na regulační rozsah, tam by to vzhledem k necitlivosti serva stejně nemělo smysl), bude třeba provést 1x 16bit unsigned inkrementaci a 7x 16bit porovnání za 20ms/1024=19.5us. Při 16MHz je to asi 312 taktů. To bys měl vpohodě zmáknout. Kdybys chtěl ještě trochu větší časovou rezervu, můžeš použít některý 20MHz mcu.

Nepočítám tady s dělením okna na jednotlivá serva, tím by se ještě dal nějaký výpočetní čas ušetřit.

Jak na to: potřebuješ generovat přerušení každých 312 taktů -> 16b timer ve FastPWM módu bez předděličky a s nastaveným TOP na 311. Tím máš vyřízena přerušení. Teď už v nich jen inkrementovat počítací proměnnou (při 1024 ji vynulovat) a porovnávat požadované pwm hodnoty jednotlivých kanálů s počítadlem. V okamžik překročení počítadla požadovanou hodnotu vynuluješ pin daného kanálu a při přetečení (z 1024 do 0) zas nastavit na 1.
To je vše, večer nemáš co dělat :wink:.

super, dakujem
nakreslil som si to a pochopil princip

Ostane mi tam aj nejaky cas na obsluhu serioveho rozhrania?
A co sa stane, ked behom prenosu (napr usart) nastane prerusenie?

Nestane se nic. Přijatý byte se uloží do UDR a do doby než přijde další (až za 1 ms pří 9600 Bd, mezi tím se ten nový skládá v posuvném registru viz. schéma usartu) tam bude čekat. Máš tedy spoustu času na jeho vyzvednutí. Kolik je ta “spousta” zjistíš, až napíšeš obsluhu pwm a změříš si v simulátoru dobu jejího zpracování :wink:.

uz druhy den preliezam tutorialove stranky ohladom timerov

co viem, treba nastavit vo funkcii main()

OCR1 = 311;

treba dovolit prerusenia

sei();

dalej treba nastavit TCCR1
ctc1 = 1 - clear timer counter - vynuluje nam TCNT1, ked sa rovna OCR1
cs10 = 1 - ziadna preddelicka,
cs11 = 0
cs12 = 0

potom nejaku nekonecnu slucku

while(1);

to by bol hlavny program

kod, ktory sa vykona ked nastane prerusenie

void ISR(TIMER1_COMPA_vect) {}

urcite mi tam nieco chyba, hlavne konfiguracia TCCR1 mi nie je jasna
v nejakom kultivovanom zapise :slight_smile:

nasiel som este nastavenie

TCCR1A = (1<<WGM11); TCCR1B = (1 << WGM13) | (1<<WGM12) | (1 << CS10);
mal by to byt fast pwm mod bez preddelicky
zajtra to skusim naostro, rozblikat nejaku diodu

Potřebuješ sice timer ve FastPWM módu, ale pouze protože tam lze nastavit TOP. HW generování pwm tě v tuhle chvíli nezajímá, budeš to muset tvořit ručně. Timer použiješ pouze pro odměření tiku a na to je přerušení od přetečení.

[code]void timerInit(void)
{
ICR1 = 311;

// Timer/Counter Mode 14 (Fast PWM, TOP=ICR1): WGM13:0 = 1110
// clk/1: CS13:0 = 001
TCCR1A |= 1<<WGM11;
TCCR1B |= 1<<WGM13 | 1<<WGM12 | 1<<CS10;

// Timer/Counter1, Overflow Interrupt Enable
TIMSK |= 1<<TOIE1;

}

ISR(TIMER1_OVF_vect)
{

}[/code]

funguje to!!! trosku som sa s tym musel pobit, ale dovolim si poslat na ukazku program ktory prijima data z usartu a generuje signal pre serva (mam vyskusane len pre jedno servo, ale urcite bude fungovat pre vsetkych 6).

zatial velke Ď

// program na ovladanie 6 servomotorov

#include<avr/io.h>
#include<avr/interrupt.h>

// Define baud rate
#define F_CPU 16000000UL
#define USART_BAUD 38400
#define USART_UBBR_VALUE F_CPU/16/USART_BAUD-1

unsigned int counter_value = 0;
unsigned int servo_value[6];

void timerInit(void) 
{ 
   // kazdych 312 taktov pri 16 mhz generuj prerusenie .. cca 19,5 us
   ICR1 = 311; 

   // Timer/Counter Mode 14 (Fast PWM, TOP=ICR1): WGM13:0 = 1110 
   // clk/1: CS13:0 = 001 
   TCCR1A |= 1<<WGM11; 
   TCCR1B |= 1<<WGM13 | 1<<WGM12 | 1<<CS10; 

   // Timer/Counter1, Overflow Interrupt Enable 
   TIMSK |= 1<<TOIE1; 
   sei();
} 

void init_uart(unsigned int ubrr) 
  {
  /* Set baud rate */
  UBRRH = (unsigned char)(ubrr>>8 ) ;
  UBRRL = (unsigned char)(ubrr);
  /* Enable receiver and transmitter */
  UCSRB |= (1<<RXEN);
  UCSRB |= (1<<TXEN);
  /* Set frame format: 8data, 2stop bit */
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
  }
  
unsigned char USART_vReceiveByte()
  {
  // Wait until a byte has been received
  while((UCSRA&(1<<RXC)) == 0);
  // Return received data
  return UDR;
  }

// ak nastane prerusenie
ISR(TIMER1_OVF_vect) 
{ 
   // ak dosiahne counter_value hodnotu serva, vynuluje sa prislusny pin
   if (counter_value == servo_value[0]) PORTB &= ~_BV(PB0); 
   if (counter_value == servo_value[1]) PORTB &= ~_BV(PB1);
   if (counter_value == servo_value[2]) PORTB &= ~_BV(PB2);
   if (counter_value == servo_value[3]) PORTB &= ~_BV(PB3);
   if (counter_value == servo_value[4]) PORTB &= ~_BV(PB4);
   if (counter_value == servo_value[5]) PORTB &= ~_BV(PB5);
   if (counter_value == 1024)
   {
      //reset counter_value
      counter_value = 0;
	  //set all servo pins to 1
      PORTB |= _BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4) | _BV(PB5);
   }
   counter_value++;
}

int main(void)
{
   unsigned char u8Data;
   unsigned int i;
   // Set PB0 - PB5 as outputs
   DDRB = 0x3F; 
   
   // Initialise USART
   init_uart(USART_UBBR_VALUE);
   
   // Initialize TIMER
   timerInit();   
   while(1)
   
   {
	//kontrola, ci prisiel spravny paket
	label:
    u8Data = USART_vReceiveByte();
    if (u8Data == 0xFF) 
      {
	  u8Data = USART_vReceiveByte(); 
      if (u8Data == 0xFF)
	    {
        u8Data = USART_vReceiveByte(); 
        if (u8Data == 0xFF)
	      {
		  u8Data = USART_vReceiveByte(); 
          if (u8Data == 0x00)
	        {
			// citaj data z usartu
			for(i=0;i<6;i++)
			  {
		      servo_value* = USART_vReceiveByte();
			  }
			}
          else goto label;
		  }
        else goto label;
		}
      else goto label;
	  }
    else goto label;
	}
}

a program na pc, ktory posiela data (servo beha hore dole)

[code]
#include <stdio.h> /* Standard input/output definitions /
#include <string.h> /
String function definitions /
#include <unistd.h> /
UNIX standard function definitions /
#include <fcntl.h> /
File control definitions /
#include <errno.h> /
Error number definitions /
#include <termios.h> /
POSIX terminal control definitions */

// ‘open_port()’ - Open serial port 1. Returns the file descriptor on success or -1 on error.
int open_port(void)
{
int fd; /* File descriptor for the port /
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/

Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);

return (fd);
}

//konfiguracia portu 38400 baud, no parity, 1 stop bit
void configure_port(void)
{
int fd; /* File descriptor for the port */
struct termios options;

/*

  • Get the current options for the port…
    */
    tcgetattr(fd, &options);

/*

  • Set the baud rates to 38400…
    */
    cfsetispeed(&options, B38400);
    cfsetospeed(&options, B38400);

/*

  • Enable the receiver and set local mode…
    */
    options.c_cflag |= (CLOCAL | CREAD);

//character size
options.c_cflag &= ~CSIZE; /* Mask the character size bits /
options.c_cflag |= CS8; /
Select 8 data bits */

//no parity
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

// Set the new options for the port…
tcsetattr(fd, TCSANOW, &options);
}

void main(void)
{
int fd = open_port();
unsigned char i = 35;
unsigned int smer = 0;
configure_port();
unsigned short int n;
while(1)
{
if (i == 35) smer = 0;
if (i == 120) smer = 1;
if (smer == 0) i++;
else i–;
unsigned char send_bytes] = { 0x00, i, 0x0F, 0xDD, 0x55, 0xEE, 0xEE, 0xFF, 0xFF, 0xFF};
n = write(fd, send_bytes, 10); //Send data
if (n < 0)
fputs(“write() of 10 bytes failed!\n”, stderr);
usleep(20000);
}
close(fd);
}
[/code]*

Používání “goto” je znakem nevhodné programové konstrukce a zde navíc zbytečné. Když ti to nevleze do ifu, pokračuje se za ním. A jelikož je if v nekonečný smyčce, bude se pokračovat na jejím začátku - tedy tam, kam ti míří “goto”.

Máš zajímavou stavbu paketu :smiley:. Co si třeba dát první byte nějakou hlavičku, pak délku a následně data. Uart je taky vhodný alespoň minimálně zabezpěčit, např. poslední byte může být xor předchozích.
Příjem dat lze taky napsat pomocí přerušení.

Takže hodnoty “counter_value” a “servo_value” budou cca od 51 do 102 pro impuls 1-2 ms (a měly by být deklarována jako uint8_t).
Tím pádem stačí začátek paketu označit jedním bajtem 0xff.

Takto se program zastaví ve funkci USART_vReceiveByte() a čeká na příjem bajtu.
Pokud by měl program kromě příjmu dat dělat i něco jiného, bylo by lepší napřed otestovat, jestli nějaký bajt přišel:

if(UCSRA & RXC) { u8Data = USART_vReceiveByte(); if (u8Data == 0xFF) { for(i=0;i<6;i++) { servo_value* = USART_vReceiveByte(); } } } *

Ta hodnota chodí do 1024, takže musí zůstat uint.

Ano, mělo tam být jenom “servo_value”.
Už jsem to včera editoval, ale asi špatně.
Ty hodnoty pro “servo_value” jsem myslím napsal dobře.

ubehlo uz dost casu, termin odovzdania sa blizi … .)

ten program sa mi podarilo rozbehnut s dvojitou presnostou 11bitov na 20ms

pridavam jedno video s ramenom, za rady Vam pekne dakujem

facebook.com/v/1658322451212

Pekne :slight_smile:

super ze si sem dal ukazku Tvojej prace a je vidiet ze rady na fore maju zmysel :slight_smile: