Skip to content

Ruanfc/a_la_ursa

Repository files navigation

Table of Contents

  1. main.c:
    1. int interruptflags
    2. void main() //main
    3. void input1() //input1
    4. void input2() //input2
    5. void adcinterrupt() //adciterrupt
    6. if (interruptflags & 1) //CanalAR
    7. if (interruptflags & 2) //CanalDE
    8. void handler2() //handler2
      1. truncamento do valor de x, quando próximo do valor de y. Isto serve para o motor não ficar trocando de polaridade repetidamente, tendo em vista que os sinais de entrada são flutuantes, mesmo que não seja intencional;
      2. Determina o sentido dos motores (Avanço ou recuo);
      3. Calcula os valores de x e y (vide: Proposta de mapeamento) e aplica como saída PWM dos motores.
    9. void handler4() //handler4
    10. void handler8() //handler8
    11. void callibrate() //callibrate
  2. userdefined.h e userdefined.c
    1. void flashww(int *Dataptr, int word) //flashww
    2. int comparar(int x, int y) //comparar
  3. multi.S:
    1. int div(int dividendo, int divisor)
    2. int mult(int a, int b)
    3. int ab(int val)

Este arquivo tem o objetivo de tentar contextualizar os membros da equipe Maracatronics sobre o projeto da ponte H do robô A La Ursa. Em caso de dúvidas, pode mandar um email. O código fonte é dividido em quatro arquivos: main.c, multi.S e userdefined.h e userdefine.c. O compilador utilizado foi o msp430-elf-gcc, sucessor do msp430-gcc (este último era utilizado antes). A seguir temos uma lista com uma breve descrição destes arquivos.

main.c:

Este é o código principal. Nele possui o procedimento principal, que é automaticamente executado pelo compilador, e possui os procedimentos que atendem as ISR’s. Os procedimentos são:

int interruptflags

Esta variável funciona como um garçom da analogia abaixo. Toda vez que uma interrupção acontece, seus bits mudam de estado para indicar que algum handler ou outro procedimento dentro do if no loop principal deve ser servido. Exemplo,

OKAY void main() //main

O main() é dividido em duas principais partes: Setup e loop. A parte do setup realiza todas as configurações iniciais para o funcionamento necessário do robô. A parte do loop fica sempre percorrendo o estado da variável interruptflags. Funciona como um menu de um restaurante, em que a CPU (o garçom da analogia) busca sempre estar disposto para atender alterações de interruptflags causadas por periféricos do microcontrolador. Ainda na analogia, o garçom trás os pratos para serem servidos, estes pratos seriam os conteúdos dos if’s no loop principal. Ao final do atendimento a CPU apaga o bit de interruptflags para indicar que a ’mesa’ já foi servida (Cada mesa seria cada tipo de evento que causa uma ISR).

KILL void input1() //input1

O canal DE do receptor (eixo y) emite ao microcontrolador sinais em PPM. Estes sinais são capturados através de detecção de borda, usando o modo de captura dos timers (#Vide slau144 no capítulo 10). Cada vez que acontece um evento de borda de subida/descida neste canal, esta rotina é chamada para salvar quanto tempo (em ciclos de máquina) se passou desde a última chamada. É através deste método que é possível ler os valores de ppm que são emitidos pelo receptor do robô. Para detalhar mais um pouco o funcionamento desta função, a cada vez que esta função é executada, a borda de captura é trocada. Com isto é possível determinar, dentre as interrupções, quando é capturado valor final ou inicial. Ao término do procedimento, um bit é resetado para indicar que aquele canal (AR) está funcionando corretamente.

DONE void input2() //input2

O procedimento input2() tem o início análogo ao input1(), porém para o canal DE (eixo x). A diferença é que esta ISR não serve apenas a IRQ de mudança de borda no canal DE (CCIFG). Além desta funcionalidade, ela serve para atender a IRQ de estouro do timerA0 (TAIFG), que é usada para manipular o comportamento de leds e para fazer polling aos níveis de bateria. Para separar estas duas funcionalidades, é usado um switch, tomando como argumento o registrador do vetor de interrupção TA0IV.

KILL void adcinterrupt() //adciterrupt

Outra ISR que devemos observar é a rotina adcinterrupt(). Esta rotina distingue através de um switch qual foi o pino que causou uma interrupção. Após isso, alguma das variáveis envolvidas recebe algum valor que depende da saída do ADC para posterior manipulação. Se for o caso do pino de FLIP (P1.0), a rotina chama o garçom para poder servir a rotina para calibrar os limites de entrada dos sinais do controle.

DONE if (interruptflags & 1) //CanalAR

Foi decidido colocar na íntegra dentro do primeiro if dentro do loop principal por ser de tamanho reduzido (3 linhas). A função deste trecho de código serve para mudar a referência do sinal recebido para o canal AR (eixo y). A idéia é que o sinal possua simetria em relação à origem (vide: Proposta de mapeamento). Observe que não importa se deltaTy é menor ou maior que zero por uma questão de complemento de 2.

DONE if (interruptflags & 2) //CanalDE

É análogo ao handler1, porém para o canal DE (eixo x).

DONE void handler2() //handler2

O handler2 é o coração do código, é parte principal do MVP. Sua função é fazer:

truncamento do valor de x, quando próximo do valor de y. Isto serve para o motor não ficar trocando de polaridade repetidamente, tendo em vista que os sinais de entrada são flutuantes, mesmo que não seja intencional;

Determina o sentido dos motores (Avanço ou recuo);

Calcula os valores de x e y (vide: Proposta de mapeamento) e aplica como saída PWM dos motores.

DONE void handler4() //handler4

O handler4 serve para gerenciar a indicação da situação atual das baterias através do led vermelho, com o devido cuidado para que o comportamento dos leds não atrapalhem um aos outros. O funcionamento deste procedimento consiste em aproveitar os estouros do TimerA0 como unidade discreta de tempo. A partir desta unidade de tempo discreta, é manipulado o tempo em que o led vermelho deve ficar aceso ou apagado.

WAIT void handler8() //handler8

O handler8() serve para reverter o sentido dos motores e trocar os lados dependendo do estado do canal 3 (canal de FLIP).

HOLD void callibrate() //callibrate

O callibrate() serve para guardar na flash os últimos valores de ppm como sendo valores extremos. Após isso, é determinado quem são os valores máximos e mínimos e os valores de offset para leitura de ppm. Esta é a forma usada para realizar calibrações. Quando o programa é inicializado, o usuário tem a oportunidade realizar uma calibração para os canais AR e DE. A visão geral é que o usuário deve manter o controle ligado com o volante e o gatilho ambos inclinados para direções arbitrárias e ao mesmo tempo o botão de reset na placa deve ser pressionado. Logo em seguida, quando ocorre a primeira leitura do FLIP, se o flipState estiver em estado alto, então uma calibração é realizada. #TODO Colocar seleção de valores máximos e mínimos também na condição state = 0.

DONE userdefined.h e userdefined.c

O arquivo .h carrega o header da biblioteca feita pelo usuário. O arquivo .c é o source da biblioteca, é neste arquivo que contém o desenvolvimento de cada função declarada em .h. A biblioteca foi criada para fins de organização. Para que o código pareça menor e menos tenebroso e então novos usuários não tenham medo de encará-los. Dentre os suas funções estão:

void flashww(int *Dataptr, int word) //flashww

Serve para escrever na memória flash de forma segura, pela flash (isto é, nenhum trecho do código é passado para RAM atráves da pilha para poder rodar algum código enquanto a gravação é realizada).

int comparar(int x, int y) //comparar

Este procedimento, em um contexto em que x e y seriam variáveis unsigned, retorna HIGH = 0xffff se (x > y). Senão, retorna LOW = 0. Pode parecer besteira ou noobice do programador, mas leia o que vem a seguir. Durante o desenvolvimento do programa, houve um problema de comparação de valores. Optva-se por usar algumas variáveis como unsigned int já que algumas variáveis costumavam ter valores altos e exclusivamente positivos. O problema surgia quando a intenção do programador era comparar dois valores os quais possuiam o bit mais significativo (MSB) diferentes. Acontece que o microcontrolador adota o teste do (MSB) como critério para identificar se o número é negativo, não importando se a variável é unsigned ou não. Dadas estas condições tornou-se necessário criar um método para comparar dois valores num contexto de valores unsigned.

DONE multi.S:

A convenção do msp430-elf-gcc para os registradores é na ordem R12, R13, R14, R15 ao invés do msp430-gcc, que é ao contrário. Este arquivo contém alguns procedimentos em que o programador optou por usar assembly. Dentre estes procedimentos estão:

int div(int dividendo, int divisor)

Retorna a divisão inteira do dividendo pelo divisor, tudo multiplicado por 216;

int mult(int a, int b)

Retorna o produto de a com b dividido por 216;

Estes procedimentos de divisão e multiplicação foram necesários porque o hardware não possui multiplicador, o compilador gera esses códigos cagados e não é uma boa ideia usar pontos flutuantes no msp430, então foi resolvido fazer o nosso próprio.

int ab(int val)

Retorna o valor absoluto do inteiro de entrada. Houve problemas em usar a biblioteca math.h, então foi decidido fazer o próprio código.

About

Código do a la ursa

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published