TickAttack e Franzininho – controlando um robô sob rodas

TickAttack e Franzininho

O Franzininho, o Arduino baseado no microcontrolador ATTiny85, pode ser considerada uma das placas mais versáteis para Arduino no Brasil. Com tamanho reduzido, ótima documentação e pelo fato de haver projetos utilizando-a estarem aparecendo, esta se configura como uma placa ideal para projetos diversos no mundo maker.

Aproveitando o surgimento e popularização desta placa, este artigo, feito numa parceria entre o Fábio Souza e eu, mostra como utilizar o gerenciador de tarefas TickAttack (portado para o ATTiny85) em conjunto com a Franzininho, ambos com a finalidade de permitir fácil e robusto controle ao Robô Franzininho. 

Material utilizado

Para reproduzir este projeto, será preciso ter à disposição os seguintes materiais:

  • Uma placa Franzininho;
  • Um Power Bank (preferencialmente um que disponibilize alimentação com corrente máxima de 2A ou superior);
  • Um módulo Bluetooth HC-05;
  • Um driver de motor com ponte H L298N;
  • Um Chassis de robô com duas rodas.

TickAttack – portabilidade para ATTiny85

Para a realização do projeto, o TickAttack foi portado para o microcontroldor ATTiny85. Em relação ao projeto original do TickAttack, devido a restrições de recursos (para se ter o menor overhead possível), os seguintes recursos foram deixados de lado:

  • Encapsulamento de variáveis relevantes ao Kernel cooperativo;
  • Mecanismo de dados compartilhados;
  • Mecanismo de medição de performance (por tarefa).

Em suma, preservou-se somente o necessário, afim de deixar a maior parte dos recursos alocáveis para o projeto principal / tarefas a serem controladas pelo TickAttack.

O TickAttack já portado para o ATTiny85 pode ser obtido do repositório oficial do projeto, clicando aqui.

Robô Franzininho – circuito esquemático

O circuito esquemático do robô Franzininho pode ser visto na figura 1.

TickAttack e Franzininho - Circuito esquemático do Robô Franzininho
Figura 1 – Circuito esquemático do Robô Franzininho

Observação: O uso do protoboard é opcional. Este foi colocado no esquema com a finalidade de facilitar a visualização.

No final da montagem, seu robô deve se parecer com o da figura 2.

TickAttack e Franzininho - Robô Franzininho montado
Figura 2 – Robô Franzininho montado

Código-fonte: TickAttack + Franzininho

Abaixo, segue o código-fonte do Robô Franzininho. O código-fonte foi feito para a Arduino IDE, portanto  a forma de programa-lo é a mesma que foi abordada no artigo original da placa.

Se preferir, pode obter o código-fonte no GitHub oficial do projeto, clicando aqui.

/*
* TickAttack for Arduino
* Author: Pedro Bertoleti
* Date: Jul/2017
*/

/*
*  IMPORTANT: 
*  1) This code was writen to be compiled for ATTINY85, using Timer1 interrupt as Tick generator. 
*     For porting it to another platforms, the major effort is replacing interrupt timer routines for 
*     those allowed in desired platform.
*  2) NEVER use delays into Tasks! A task manager tries to execute all the tasks
*     in real-time and in the "time to execute" it´s needed. A delay will certanly 
*     spoil it all. 
*     NEVER use blocking functions inside tasks.
*     AVOID USING very time expensive loops inside tasks.  
*/

/*
* Using this Task Manager
* 
* Write inside Task1, Task2, Task3 and Task4 functions what must be done
* in these tasks (first of all, read IMPORTANT item #2. Do not ignore what
* is written there).
*
* If you need more tasks, simply write more functions according to Tasks´ 
* function modules (in other words, copy, paste, change the name, declare
* prototypes and that´s it!). Please, do not forget to refresh NUMBER_OF_TASKS define
* and create a define for your tasks´ index too.
*/
#include <SoftwareSerial.h>
#include "avr/wdt.h"

/*
*  Defines
*/
#define INDEX_TASK_1                 0
#define INDEX_TASK_2                 1
#define INDEX_TASK_3                 2
#define INDEX_TASK_4                 3
#define NUMBER_OF_TASKS                4
#define TIME_TO_EXECUTE_TASK1          100   //time unit: ms
#define TIME_TO_EXECUTE_TASK2          500  //time unit: ms
#define TIME_TO_EXECUTE_TASK3          1000  //time unit: ms
#define TIME_TO_EXECUTE_TASK4          1000  //time unit: ms
#define TASK_TIMEOUT                   1000  //time unit: ms

#define YES                        1
#define NO                         0

/*
*  Global variables
*/
void (*ScheduledTasks[NUMBER_OF_TASKS])(void);  //It stores the function pointers of Task.
int TimeScheduledTasks[NUMBER_OF_TASKS];        //It stores the task´s times (time period to execute)
int RecentTasksTimeToExecute[NUMBER_OF_TASKS];  //It stores the recent task´s times ("time to execute" each task)
char TimerIntGeneraed;                          //It indicates if  timer interrupt has been generated
char TaskIsExecuting;                           //It indicates if a task in executing (important information for consider task timeout validation)
int TaskTimeout;                                //used for timeout counting
SoftwareSerial mySerial(2, 6);                  //pin 2(RX) and pin 6 (TX) - software serial
char IsThereCommandToBeCleared;                 //It indicates if there's a command to be cleared (so the robot won't repeat "forever" the lst command sent)

/*
*  Constants
*/
const byte PIN_A = 0;
const byte PIN_B = 1;
const byte PIN_C = 3;
const byte PIN_D = 4;


/*
*  Prototypes
*/
void InitTasks(void);
void ExecuteTask(void);
void Task1(void);       //task number one - write into this function what this taks will make
void Task2(void);       //task number two - write into this function what this task will make
void Task3(void);       //task number three - write into this function what this task will make
void Task4(void);       //task number four - write into this function what this task will make

/*
* Application function prototypes
*/
void SetupApplication(void);
void GoForward(void);
void GoBackwards(void);
void GoLeft(void);
void GoRight(void);
void StopRobot(void);

/*
*   Functions
*/

/*
*   Applications Functions
*/

void SetupApplication(void)
{
  pinMode(PIN_A, OUTPUT);
  pinMode(PIN_B, OUTPUT);
  pinMode(PIN_C, OUTPUT);
  pinMode(PIN_D, OUTPUT);
  mySerial.begin(9600);
}

void GoForward(void)
{
  digitalWrite(PIN_A, HIGH);
  digitalWrite(PIN_B, LOW);
  digitalWrite(PIN_C, HIGH);
  digitalWrite(PIN_D, LOW);
}

void GoBackwards(void)
{
  digitalWrite(PIN_A, LOW);
  digitalWrite(PIN_B, HIGH);
  digitalWrite(PIN_C, LOW);
  digitalWrite(PIN_D, HIGH);
}

void StopRobot(void)
{
  digitalWrite(PIN_A, LOW);
  digitalWrite(PIN_B, LOW);
  digitalWrite(PIN_C, LOW);
  digitalWrite(PIN_D, LOW);
}

void GoRight(void)
{
  digitalWrite(PIN_A, LOW);
  digitalWrite(PIN_B, HIGH);
  digitalWrite(PIN_C, HIGH);
  digitalWrite(PIN_D, LOW);
}

void GoLeft(void)
{
  digitalWrite(PIN_A, HIGH);
  digitalWrite(PIN_B, LOW);
  digitalWrite(PIN_C, LOW);
  digitalWrite(PIN_D, HIGH);
}

/*
*   TickAttack Functions
*/

//Function: initialize and schedule tasks
//Params: nothing
//Return: nothing
void InitTasks(void)
{
    //init function pointers of tasks
    ScheduledTasks[INDEX_TASK_1] = Task1;
    ScheduledTasks[INDEX_TASK_2] = Task2;
    ScheduledTasks[INDEX_TASK_3] = Task3;
    ScheduledTasks[INDEX_TASK_4] = Task4;
	
    //init temporization values of each task. These values do no change during execution
    TimeScheduledTasks[INDEX_TASK_1] = TIME_TO_EXECUTE_TASK1;
    TimeScheduledTasks[INDEX_TASK_2] = TIME_TO_EXECUTE_TASK2;
    TimeScheduledTasks[INDEX_TASK_3] = TIME_TO_EXECUTE_TASK3;
    TimeScheduledTasks[INDEX_TASK_4] = TIME_TO_EXECUTE_TASK4;
	
    //init recent temporization values of each task. These values will change during execution (they´re used to decide which task must be executed)
    RecentTasksTimeToExecute[INDEX_TASK_1] = TIME_TO_EXECUTE_TASK1;
    RecentTasksTimeToExecute[INDEX_TASK_2] = TIME_TO_EXECUTE_TASK2;
    RecentTasksTimeToExecute[INDEX_TASK_3] = TIME_TO_EXECUTE_TASK3;	
    RecentTasksTimeToExecute[INDEX_TASK_4] = TIME_TO_EXECUTE_TASK4;	
    
    //It indicates that there´s no task executing
    TaskIsExecuting = NO;
}

//Function: Task 1 function (reads serial incoming data and control robot)
//Params: nothing
//Return: nothing
void Task1(void)
{
  if (mySerial.available())
  {
    //reads serial data
    int cmd = mySerial.read();
    IsThereCommandToBeCleared = YES;
    
    //controlling robot
    switch(cmd)
    {
      case 'a':
	    GoForward();
            break;
      case 'b':
	    GoLeft();
            break;
      case 'd':
	    GoRight();
            break;
      case 'e':
	    GoBackwards();
            break;
      case 'c':
	    StopRobot();
            break;
      default:
            IsThereCommandToBeCleared = NO;
            break;
    }
  }
}

//Function: Task 2 function (clear a command sent previously to robot)
//Params: nothing
//Return: nothing
void Task2(void)
{
    if (IsThereCommandToBeCleared == YES)
       StopRobot(); 
}

//Function: Task 3 function
//Params: nothing
//Return: nothing
void Task3(void)
{
    
}

//Function: Task 4 function
//Params: nothing
//Return: nothing
void Task4(void)
{
    
}

void setup()
{   
  //Init/configures all tasks
  InitTasks(); 

  //Init/configures application variables and functions
  IsThereCommandToBeCleared = NO;
  SetupApplication();


  //setup TIMER 1 = Toverflow = (Timer Max Value(255) x prescaler(32))/fosc(8 MHz) = 1,02 ms
  TCCR1 = (1<<CS12)|(1<<CS11);    //Prescale = 32
  TIMSK = 1<<TOIE1;               //Timer 1 Overflow Interrupt Enable
  sei();                          //Globla interrupt Enable
  
  TimerIntGeneraed = NO;
}

//Function: timer 1 isr
//Params: nothing 
//Return: nothing
ISR(TIMER1_OVF_vect)  
{
    char i;
    
    TimerIntGeneraed = YES;

    //Here, the "time to execute" of each task is refreshed

    for (i=0; i<NUMBER_OF_TASKS; i++)
    {
	if (RecentTasksTimeToExecute[i] > 0)
  	  RecentTasksTimeToExecute[i]--;			
    }
			
    if (TaskIsExecuting == YES)
    {
        TaskTimeout--;
            
         if (!TaskTimeout)
         {
             //Timeout has reached (possibly a task has crashed. Microcontroller must be reset)
             wdt_enable(WDTO_15MS); 
             while(1);   
         }
    }
}


//Function: Execute tasks
//Params: nothing
//Return: nothing
void ExecuteTask(void)
{
    char i;
    
    for (i=0; i<NUMBER_OF_TASKS; i++)
    {
	//Check if it´s time to execute a task
	if ((ScheduledTasks[i] != 0) && (RecentTasksTimeToExecute[i] == 0))
        {
  	    TaskIsExecuting = YES;
            TaskTimeout = TASK_TIMEOUT;
            ScheduledTasks[i]();  //executes the task
            TaskIsExecuting = NO;
	    RecentTasksTimeToExecute[i] = TimeScheduledTasks[i];  //reagendamento da tarefa		
        }
    }
}

void loop()
{
    if ((TimerIntGeneraed == YES)  && (NUMBER_OF_TASKS)) 
    {
        TimerIntGeneraed = NO;  
        ExecuteTask();			
    }
}

Vídeo – Robô Franzininho em ação!

Veja abaixo um vídeo do Robô Franzininho em ação:

Vá além: utilize a Franzininho + TickAttack nos seus projetos

Neste artigo foi mostrado um uso divertido e adequado para o TickAttack em conjunto com a Franzininho: o controle do Robô Franzininho, um robô sob rodas. Agora que você já sabe como desenvolver um projeto utilizando-os, o céu é o limite!

Portanto, explore sem medo o TickAttack em conjunto com os recursos da Franzininho o quanto puder, desde robôs e controles de I/O até sistemas supervisórios. E, claro, compartilhe com a comunidade, contribuindo assim com o projeto e novas ideias! 

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
0 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Software » TickAttack e Franzininho – controlando um robô sob rodas

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: