Raspberry Pi

Beagleboard-xM + Trainer-xM Board.

La trainer-xM board de tincantools.com es el complemento ideal para añadir la Beagleboard a nuestros proyectos electrónicos.


Se trata de una placa que se inserta en el conector de expansión de la BB-xM y sirve de interfaz para la electrónica que queramos usar con la beagle. El micro de la beagle funciona a 1.8V y por lo general la electrónica que usamos en nuestros proyectos funciona a 3.3 y 5V,  por lo que es necesario un circuito que convierta los niveles de tensión de los pines de entrada/salida de la beagle de 1.8V a 3.3/5V y viceversa.

La trainer-xM convierte los pines de entrada y salida de la beagle del conector de expansión de 1.8V a niveles de 3.3V, proporciona al micro un adaptador de niveles para el bus SPI a 3.3V, otro para el bus I2C que puede funcionar a 3.3V ó 5V según un jumper, y también nos incluye un microcontrolador ATmega328p a 8 MHz conectado a una de las UARTs de la beagleboard, el ATmega puede funcionar a 5V ó 3.3V según otro jumper.

Este microcontrolador lleva grabado el bootloader de Arduino por lo que creo (no soy muy seguidor de Arduino) que se puede programar directamente desde la beagle al estar conectada su UART a la UART de la beagleboard. También lleva el conector ISP que nos permite quitar el bootloader de Arduino para conectar un programador externo y grabar nuestros programas directamente.

La adición del ATmega a la placa es muy útil ya que nos permite delegar en él el control de la electrónica a bajo nivel. Por ejemplo en un robot que estoy haciendo a partir de un coche RC voy a usar la beagleboard como cerebro.

Para que el coche/robot pueda funcionar hay que controlar dos motores a través de dos puentes en H, 3 servomotores para el giro de la dirección y de la cámara, y leer varios valores analógicos del consumo de intensidad de los motores y del nivel de la batería. Toda esta electrónica funciona a 5V, la idea es tener un microcontrolador que se encargue de leer los valores analógicos a través de su ADC y del control de los distintos componentes del coche según las ordenes que recibe desde la beagleboard. 

Para hacer todo el conexionado del ATmega (o de los pines/buses de salida de la beagle) al resto de la electrónica la trainer-xm cuenta con una zona de topos, donde soldamos los conectores o componentes que queramos y los unimos con los pines de entrada/salida del ATmega y la beagle según nos interese.

Como se puede ver en la foto la placa encaja debajo de la beagle por lo que minimiza el espacio necesario.

Respecto a la beagleboard nos viene con un sistema operativo de prueba, por lo que lo primero que debemos hacer es instalar alguno de los muchos que andan por internet. Yo con la idea de aprender algo de linux pues me he decicido a instalar uno, tras muchos post que llevamos en el foro (hay que googlear mucho) voy a empezar con ubuntu.

No todas las versiones que descarguemos funcionan bien ya que hay varias versiones de las placas, por ejemplo de este ubuntu a Raúl con la BB-xM RevB le funcionaba sin problemas cuando lo probó, a mi con la misma placa RevC no me inicializaba el hardware, tenía que arrancar con otro SO para inicializar el hardware de la placa, y cambiar la tarjeta del SO sin apagar y resetear para que funcionase todo correctamente.

Después de buscar yo me he decidido por instalar este ubuntu, sólo hay que descargar la imagen y seguir los pasos que ahí se indican desde un ordenador con linux para grabar la tarjeta, meterla en la beagle y con una conexión a internet seguir las instrucciones que se dan si se quiere instalar el entorno gráfico, todo muy sencillo y funciona a la primera.

Como se puede ver en la imagen (click para mayor tamaño) el micro de la beagle funciona a 600 MHz en lugar de a 1 GHz, por aquí está notificado, no lo he hecho pero se puede cambiar el mpurate en /boot/boot.scr a 800 MHz para que funcione a esta velocidad en lugar del 1 GHz que no coge.

Para comunicarnos con el ATmega de la trainer-xM y comprobar el funcionamiento de la comunicación con la beagle podemos hacer un programa simple en el ATmega que grabamos en él a través del conector ISP, por ejemplo una comunicación sin interrupciones para probar, donde el ATmega recibe una letra del teclado desde la beagle y nos devuelve la letra enviada y la dos siguientes en la consola.

//AVR BB-XM, interfaz electrónica.
#define F_CPU 8000000UL	
#define FOSC 8000000UL// Clock Speed
#define BAUD 9600
#define MYUBRR ((F_CPU/(BAUD*16UL))-1)
 
 
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
 
//Leds.
#define	LEDR PORTB2 //Led rojo
#define	LEDV PORTB4 //Led verde
 
 
void inicializar_hardware(void);
void USART_Transmit( unsigned char data );
unsigned char USART_Receive( void );
 
 
int main( void ) 
{
	inicializar_hardware();
 
	unsigned char dato;
 
	PORTB |= (1<<LEDV);
 
	while ( 1 ) 
	{	
 
	dato = USART_Receive();
	USART_Transmit(dato);
	USART_Transmit(++dato);
	USART_Transmit(++dato);
	USART_Transmit(' ');
 
 
	PORTB ^= 0x14;
	}
	return 0;
}
 
void inicializar_hardware(void)
{   
   DDRB=0x14;     //0001 0100  
   PORTB=0x00;
 
   //Inicializar usart.
   UBRR0H = (MYUBRR>>8);
   UBRR0L = MYUBRR;
   /* Enable receiver and transmitter */
   UCSR0B = (1<<RXEN0)|(1<<TXEN0);
   /* Set frame format: 8data, 2stop bit */
   UCSR0C = (1<<USBS0)|(3<<UCSZ00)|(1<<UCSZ01);
 
}
 
void USART_Transmit( unsigned char data )
{
   /* Wait for empty transmit buffer */
   while ( !( UCSR0A & (1<<UDRE0)) );
   /* Put data into buffer, sends the data */
   UDR0 = data;
}
 
unsigned char USART_Receive( void )
{
	/* Wait for data to be received */
	while ( !(UCSR0A & (1<<RXC0)) );
	/* Get and return received data from buffer */
	return UDR0;
}

 Para comunicarnos con el micro según me ha recomendado Óscar instalamos screen en la beagle, en un terminal: sudo apt-get install screen. Y establecemos la comunicación a la velocidad que hemos programado en el ATmega, en este caso en un terminal escribimos: screen /dev/ttyO1 9600, viendo como al pulsar y enviar una tecla el ATmega nos devuelve la tecla pulsada y las dos siguientes según lo programado.

 La Beagleboard-xM más la Trainer-xM son ideales para controlar un robot y para aprender sobre estas cosas, la comunidad es muy grande por lo que puede que sea de las mejores placas para empezar.

Una foto de como va quedando mi BeagleBot =)

 

Edito: video de prueba de la electrónica controlada desde un teclado conectado a la beagleboard.

Programa del ATmega para comprobar el funcionamiento de la electrónica.

//AVR BB-XM, interfaz electrónica.
#define F_CPU 8000000UL	
#define FOSC 8000000UL// Clock Speed
#define BAUD 9600
#define MYUBRR ((F_CPU/(BAUD*16UL))-1)
 
 
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
 
//Pines entrada y salida.
#define	LEDR PORTB2 //Leds
#define	LEDV PORTB4 
#define SERVO0 PORTC0 //Servomotores 
#define SERVO1 PORTC1
#define SERVO2 PORTC2
#define SERVO3 PORTB5
#define DIR1 PORTB0 //Puente en H 1
#define PWM1L PORTD3
#define PWM1H PORTB3
#define RESET1 PORTB1
#define SENSOR1 PORTC4
#define BATERIA PORTC5
#define DIR2 PORTD4 //Puente en H 2
#define PWM2L PORTD5
#define PWM2H PORTD6
#define RESET2 PORTD7
#define SENSOR2 PORTC3
 
 
void inicializar_hardware(void);
void USART_Transmit( unsigned char data );
unsigned char USART_Receive( void );
 
void M1_pwm(unsigned char pwm, char direccion, char reset);
void M2_pwm(unsigned char pwm, char direccion, char reset);
 
 
volatile unsigned int duty0 = 1500;
volatile unsigned int duty1 = 1500;
volatile unsigned int duty2 = 1500;
volatile unsigned int duty3 = 1500;
 
 
int main( void ) 
{
	inicializar_hardware();
 
	char comando; //comando recibido
	unsigned char giro = 7; //giro seleccionado
    int pwm = 0; //velocidad de los motores
	int pwm2 = 0;
	unsigned int servo0_centro = 1500;
	unsigned int servo1_centro = 1500;
//	unsigned int servo2_centro = 1500;
	unsigned int servo3_centro = 1500;
 
	PORTB |= (1<<LEDV);
 
	M1_pwm(0,0,1);
	M2_pwm(0,0,1);
 
	while ( 1 ) 
	{		
	comando = USART_Receive();
 
	switch (comando)
	{
		case '8': //Sumar +x al avance del motor
		{
 
			pwm = pwm + 25;
 
			if (pwm > 255)
				pwm = 255;
 
			if (pwm >= 0)
			{
				M1_pwm((char)pwm,0,1);	
				M2_pwm((char)pwm,0,1);
			}
			else
			{
				pwm2 = pwm;
				pwm2 = pwm2 * (-1);
				M1_pwm((char)pwm2,1,1);	
				M2_pwm((char)pwm2,1,1);
			}
 
			USART_Transmit('8');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
 
		} 
		break;
 
		case '2': //Sumar -x al avance del motor
		{
 
			pwm = pwm - 25;
 
			if (pwm < -255)
				pwm = -255;
 
			if (pwm >= 0)
			{
				M1_pwm((char)pwm,0,1);	
				M2_pwm((char)pwm,0,1);
			}
			else
			{
				pwm2 = pwm;
				pwm2 = pwm * (-1);
				M1_pwm((char)pwm2,1,1);	
				M2_pwm((char)pwm2,1,1);
			}
 
			USART_Transmit('2');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '4': //Sumar +x al servo de dirección
		{
 
			switch(giro)
			{
 
				case 1: //Giro doble
				{
					duty0 = duty0 + 50;
					if (duty0 > 2000)
						duty0 = 2000;
					duty1 = duty1 - 50;
					if (duty1 < 1000)
						duty1 = 1000;					
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 3: //Desplazamiento
				{
					duty0 = duty0 + 50;
					if (duty0 > 2000)
						duty0 = 2000;
					duty1 = duty1 + 50;
					if (duty1 > 2000)
						duty1 = 2000;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 7:
				{
					duty0 = duty0 + 50;
					if (duty0 > 2000)
						duty0 = 2000;
					duty1 = servo1_centro;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 9:
				{
					duty1 = duty1 + 50;
					if (duty1 > 2000)
						duty1 = 2000;
					duty0 = servo0_centro;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
 
				}
				break;
 
				default:
				{
					PORTB |= (1<<LEDR);
					PORTB &= ~(1<<LEDV);
				}
				break;
			}
 
			USART_Transmit('4');
		} 
		break;
 
		case '6': //Sumar -x al servo de dirección 
		{
 
			switch(giro)
			{
 
				case 1: //Giro doble
				{
					duty0 = duty0 - 50;
					if (duty0 < 1000)
						duty0 = 1000;
					duty1 = duty1 + 50;
					if (duty1 > 2000)
						duty1 = 2000;					
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 3: //Desplazamiento
				{
					duty0 = duty0 - 50;
					if (duty0 < 1000)
						duty0 = 1000;
					duty1 = duty1 - 50;
					if (duty1 < 1000)
						duty1 = 1000;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 7:
				{
					duty0 = duty0 - 50;
					if (duty0 < 1000)
						duty0 = 1000;
					duty1 = servo1_centro;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
				}
				break;
 
				case 9:
				{
					duty1 = duty1 - 50;
					if (duty1 < 1000)
						duty1 = 1000;
					duty0 = servo0_centro;
 
					PORTB |= (1<<LEDV);
					PORTB &= ~(1<<LEDR);
 
				}
				break;
 
				default:
				{
					PORTB |= (1<<LEDR);
					PORTB &= ~(1<<LEDV);
				}
				break;
			}
				USART_Transmit('6');
		} 
		break;
 
		case '5': //Frenar motores
		{
 
			M1_pwm(0,0,1);	
			M2_pwm(0,0,1);
			pwm = 0;
 
 
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case ' ': //Frenar motores y centrar servos
		{
			USART_Transmit(' ');
 
			M1_pwm(0,0,1);	
			M2_pwm(0,0,1);
			pwm = 0;
			duty0 = servo0_centro;
			duty1 = servo1_centro;
//			duty2 = servo2_centro;
			duty3 = servo3_centro;
 
			USART_Transmit(' ');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '7': //Giro 1
		{
 
			giro = 7;
 
			USART_Transmit('7');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '9': //Giro 2
		{
 
			giro = 9;
 
			USART_Transmit('9');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '1': //Giro doble
		{
 
			giro = 1;
 
			USART_Transmit('1');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '3': //Desplazamiento
		{
 
			giro = 3;
 
			USART_Transmit('3');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '0': //Rotar cámara e invertir giro
		{
			USART_Transmit('0');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '*': //Sumar +x servo cámara
		{
 
			duty2 = duty2 + 25;
			if (duty2 > 2000)
				duty2 = 2000;
 
			USART_Transmit('*');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		case '/': //Sumar -x servo cámara
		{
 
 
			duty2 = duty2 - 25;
			if (duty2 < 1000)
				duty2 = 1000;
 
			USART_Transmit('/');
			PORTB |= (1<<LEDV);
			PORTB &= ~(1<<LEDR);
		} 
		break;
 
		default:
		{
			PORTB |= (1<<LEDR);
			PORTB &= ~(1<<LEDV);
		}
		break;
	}
 
//	USART_Transmit(dato);
//	USART_Transmit(++dato);
//	USART_Transmit(++dato);
//	USART_Transmit(' ');
//	PORTB ^= 0x14;
	}
	return 0;
}
 
void inicializar_hardware(void)
{   
    DDRB=0x3F;     //0011 1111
    PORTB=0x00;
 
    DDRC=0x07;     //0000 0111 
	PORTC=0x00;
 
    DDRD=0xF8;     //1111 1000  
    PORTD=0x00;
 
    //Inicializar usart.
    UBRR0H = (MYUBRR>>8);
    UBRR0L = MYUBRR;
    /* Enable receiver and transmitter */
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	/* Set frame format: 8data, 2stop bit */
	UCSR0C = (1<<USBS0)|(3<<UCSZ00)|(1<<UCSZ01);
 
	//Inicializar PWM motores
	// configure for inverted PWM output on motor control pins:
	// set OCxx on compare match, clear on timer overflow
	// Timer0 and Timer2 count up from 0 to 255
	TCCR0A = TCCR2A = 0xA3;
	// use the system clock/64 (=125 KHz) as the timer clock
	TCCR0B = TCCR2B = 0x03;
	// initialize all PWMs to 0% duty cycle 
	OCR0A = OCR0B = OCR2A = OCR2B = 0;
	// set PWM pins as digital outputs (the PWM signals will not
	// appear on the lines if they are digital inputs)
	DDRD |= (1 << PORTD3) | (1 << PORTD5) | (1 << PORTD6);
	DDRB |= (1 << PORTB3);
 
	//Inicializar timer1
    OCR1A= 0x03E8; // 1 tic = 1 us 0x0FA0 4ms  0x1388=5ms
    TCCR1B |=((1<<WGM12)|(1<<CS11));    //Los bits que no se tocan a 0 por defecto
    TIMSK1 |= (1<<OCIE1A);
    sei();
 
 
 
}
 
void USART_Transmit( unsigned char data )
{
   /* Wait for empty transmit buffer */
   while ( !( UCSR0A & (1<<UDRE0)) );
   /* Put data into buffer, sends the data */
   UDR0 = data;
}
 
unsigned char USART_Receive( void )
{
	/* Wait for data to be received */
	while ( !(UCSR0A & (1<<RXC0)) );
	/* Get and return received data from buffer */
	return UDR0;
}
 
//Interrupción del timer1, genera la señal de control
//de los servomotores. 4 servos, periodo 20 ms.
ISR(TIMER1_COMPA_vect)
{
	static char estado = 0;
 
	switch(estado)
	{
		case 0: //ancho de pulso del servo0
		{
		    OCR1A = duty0;	
			PORTC |= (1<<SERVO0);
			estado++;
		}
		break;
 
		case 1: //resto de tiempo del servo0
		{
		    OCR1A = 5000 - duty0;	
			PORTC &= ~(1<<SERVO0);	
			estado++;
		}
		break;
 
		case 2: //ancho de pulso del servo1
		{
		    OCR1A = duty1;	
			PORTC |= (1<<SERVO1);	
			estado++;
		}
		break;
 
		case 3: //resto de tiempo del servo1
		{
		    OCR1A = 5000 - duty1;
			PORTC &= ~(1<<SERVO1);			
			estado++;
		}
		break;
 
		case 4: //ancho de pulso del servo2
		{
		    OCR1A = duty2;
			PORTC |= (1<<SERVO2);			
			estado++;
		}
		break;
 
		case 5: //resto de tiempo del servo2
		{
		    OCR1A = 5000 - duty2; 
			PORTC &= ~(1<<SERVO2);			
			estado++;
		}
		break;
 
		case 6: //ancho de pulso del servo3
		{
		    OCR1A = duty3;	
			PORTB |= (1<<SERVO3);
			estado++;
		}
		break;
 
		case 7: //resto de tiempo del servo4
		{
		    OCR1A = 5000 - duty3;
			PORTB &= ~(1<<SERVO3);			
			estado = 0;
		}
		break;
 
		default:
		{
			PORTB |= (1<<LEDR);
			PORTB &= ~(1<<LEDV);
		}
		break;
	}
 
	//no se tiene en cuenta el tiempo de ejecución del switch
	//en la posición del servo, us despreciables.	
 
    TIFR1 |= (1<<OCF1A);
}
 
 
//Funciones para el control de los motores, falta las de frenar.
void M1_pwm(unsigned char pwm, char direccion, char reset)
{
    OCR0A = pwm;
    OCR0B = pwm;
 
	if (direccion == 0)
			PORTB &= ~(1<<DIR1);
	else
			PORTB |= (1<<DIR1);	
	if (reset == 0)
			PORTB &= ~(1<<RESET1);
	else
			PORTB |= (1<<RESET1);
 
}
void M2_pwm(unsigned char pwm, char direccion, char reset)
{
    OCR2B = pwm;
    OCR2A = pwm;
 
	if (direccion == 0)
			PORTD &= ~(1<<DIR2);
	else
			PORTD |= (1<<DIR2);	
	if (reset == 0)
			PORTD &= ~(1<<RESET2);
	else
			PORTD |= (1<<RESET2);
}
Visitas :8838
Responses are currently closed, but you can trackback from your own site.

One Response to “Beagleboard-xM + Trainer-xM Board.”

  1. Hanssey says:

    Hey, it looks nice. ;-)

Subscribe to RSS Feed Follow me on Twitter!