Jump to content
Silva Manuel

Ajuda num loop "while, Switch, if else, case"

Recommended Posts

Silva Manuel

Boas a todos.

Este é o meu primeiro programa em C. Estou a ter bastantes dificuldades e estou a tentar resolver este problema há vários dias sem sucesso. Estou a tentar ler uma "string" em formato binário, enviada por um GPS com a cadência de 1 seg. através da porta USART. O cabeçalho da string é 0xB5, 0x62, 0x01, 0x05. O que tento fazer é identificar o início da "string" lendo os primeiros 3 bytes em sequência, ficando à espera que apareça a sequência correcta para começar a guardar os dados.

Em primeiro lugar tive dificuldade em definir os tipos de variáveis, com alguns tipos, as mesmas não eram armazenadas (mas já não sei quais e que tipo).

Penso que tenho um problema na estrutura, porque, em algumas situações de "break"  (penso que "case 2" ) em vez de sair do "switch" vai para o "case 3" e incrementa o apontador do array "data_count". Penso que tenho também problemas relativamente a "{}" na definição dos vários loops.

Vou colocar as definições das variáveis e a parte do "switch".

Antecipadamente agradeço a ajuda.

Cumprimentos

Manuel

Variáveis:

 static uint8_t GPS_data[28]; 
volatile uint8_t rx_state = 0,data_count = 0; 
volatile uint16_t time;
uint8_t checksum_a=0;
uint8_t checksum_b=0;
volatile uint8_t received_byte;  

Programa:

    
	for(;
	{

uint8_t n=1;
static uint8_t GPS_data[28]; 
  	static uint8_t rx_state = 0,data_count = 0; 
// 	static uint8_t received_byte=0; 
  
   while (n)
{

   	while (!(UCSR0A & (1<<RXC0))){}
received_byte= UDR0;

      switch(rx_state) 
         { 
         case 0: //wait for first byte 0xb5 
             if (received_byte == 0xb5) 
                { 
               rx_state = 1; 
               } 
            break; 
         case 1: //wait for second byte 0x62 
             if (received_byte == 0x62) 
                { 
               rx_state = 2; 
               } 
             else 
                { 
               rx_state = 0; 
               } 
            break; 
         case 2:				 //wait for third byte 0x01 
             if (received_byte == 0x01) 
                { 
               rx_state = 3; 
               data_count = 0; 
               } 
             else 
                { 
               rx_state = 0; 
               } 
            break; 
         case 3: 								//accumulate the packet data 
             GPS_data[data_count++] = received_byte; 
             if (data_count > 29) 
                { 
              	 checksum_a=checksum_a+received_byte;
			 data_count=0;    //check checksum, set packet flag etc... 
               	 rx_state = 0; //go back to looking for another packet 
			 n=0;
              	 } 
             break; 
         }//end switch 
}             

Share this post


Link to post
Share on other sites
daj

Se entendi correctamente o teu código, não estás a ter em conta situações do género 0xB5, 0x62, 0xB5, 0x62, 0x01 em que o terceiro byte é inválido no estado rx_state a 2 mas seria válido no rx_state a 0.

Para além disso, na situação em que data_count é 27, o caso 3 do switch escreve no último índice de GPS_data (27) e incrementa data_count para 28. Não sendo maior que 29, dá mais uma volta no ciclo. Algo semelhante acontece na iteração seguinte e só na outra a seguir terminas o ciclo. Se estou correcto na minha análise, estás a escrever fora do array.

De qualquer das maneiras sugeria algo que me parece mais simples:

uint8_t sig[] = { 0xB5, 0x62, 0x01 };
int read_sig = 0;

while  (28 != data_count) {
    while (!(UCSR0A & (1<<RXC0))) { }
    received_byte = UDR0;

    if (read_sig < 3)
        read_sig = received_byte == sig[read_sig] ? read_sig + 1 : received_byte == sig[0];
    else
        GPS_data[data_count++] = received_byte;
}

Aplicam-se os avisos habituais de que o código sugerido pode não funcionar nem sequer fazer sentido. :-)

Share this post


Link to post
Share on other sites
Silva Manuel

Obrigado pelas vossas respostas.

Josevellozo, o "while" abaixo funciona, o problema que tenho é que em algumas situações de "break" o programa em vez de sair do "switch" vai para o "case 3" e incrementa o apontador do array "data_count" guardando dados errados.

   while (!(UCSR0A & (1<<RXC0))){}
        received_byte= UDR0;

Daj, muito obrigado pela sua sugestão. Como disse, este é o meu primeiro programa. Vou testar a sua sugestão e darei notícias. Relativamente à classificação das variáveis (volatile, static, etc), tenho que fazer alguma alteração. Já agora agradecia a sua sugestão.

Obrigado.

Manuel

Share this post


Link to post
Share on other sites
daj

Aqui era mesmo isto que queria fazer?

 while (!(UCSR0A & (1<<RXC0))){}
        received_byte= UDR0;

Porque se eu percebi bem, nenhuma instrução está a ser executada no ciclo while

No while estão a ser lidos dados nos endereços indicados por UCSR0A e RXC0, que certamente estão associados a dispositivos externos e são volatile.

Relativamente à classificação das variáveis (volatile, static, etc), tenho que fazer alguma alteração.

No código da primeira mensagem existe uma repetição nas declarações das variáveis, umas com static e outras com volatile e não sei qual delas está a ser utilizada. Não creio que seja necessário ter nenhuma variável com o atributo volatile, a não ser os endereços dos dispositivos externos (UCSR0A, RXC0 e UDR0), que provavelmente fazem parte de uma biblioteca já feita. O static também não me parece necessário.

Ainda sobre o código da mensagem original, e resumindo o que disse anteriormente, o único problema que encontro é a escrita para lá do array GPS_data (com 28 posições, só faz sentido escrever de GPS_data[0] até GPS_data[27]). Fica agora a questão sobre que dados estão na memória ao lado de GPS_data e estão a ser destruídos, o que poderá explicar o comportamento erróneo.

Share this post


Link to post
Share on other sites
bubulindo

No while estão a ser lidos dados nos endereços indicados por UCSR0A e RXC0, que certamente estão associados a dispositivos externos e são volatile.

Não, os enderecos indicados por UCSR0A e RXC são internos e estão definidos no include da biblioteca <avr/io.h>. Na realidade são defines.

Eu sei que isto é o fórum de C, mas esta dúvida é no mínimo dividida entre electrónica e C uma vez que é código dum microcontrolador.

O que o while está a fazer é esperar que o programa receba uma string por porta série, daí que o while, como já foi dito, realmente esteja a fazer algo. O problema com esta implementacão é que o programa fica parado à espera de receber caracteres e quando recebe dados vai fazer todo um conjunto de tarefas e depois volta a ver se recebeu outro valor na porta série... A porta série do microcontrolador não é bufferizada e existe assim a hipótese de perder bytes por serem escritos uns sobre os outros.

Uma melhor hipótese é utilizares a porta série com interrupcões e criares um bounded buffer. Com alguma pesquisa (procura por peter fleury na net), consegues exemplos que com muito poucas ou nenhumas modificacões funcionam em todos os AVRs. Depois apenas tens de inserir uma condicão na rotina de recepcão da porta série que quando receber o caracter de fim da string do GPS, anuncie ao programa principal que existem dados prontos a consumir.

Criar uma variável de contagem, por exemplo... ou um bit.

No código da primeira mensagem existe uma repetição nas declarações das variáveis, umas com static e outras com volatile e não sei qual delas está a ser utilizada. Não creio que seja necessário ter nenhuma variável com o atributo volatile, a não ser os endereços dos dispositivos externos (UCSR0A, RXC0 e UDR0), que provavelmente fazem parte de uma biblioteca já feita. O static também não me parece necessário.

Ainda sobre o código da mensagem original, e resumindo o que disse anteriormente, o único problema que encontro é a escrita para lá do array GPS_data (com 28 posições, só faz sentido escrever de GPS_data[0] até GPS_data[27]). Fica agora a questão sobre que dados estão na memória ao lado de GPS_data e estão a ser destruídos, o que poderá explicar o comportamento erróneo.

Pelo que vi, os volatiles não são necessários e não vejo qual é o interesse de ter variáveis static...

Eu não estou completamente dentro dos GPS, mas se forem mensagens do tipo NMEA, muito provavemente terás de ter um buffer bem maior para veres o resultado.

Isto, por exemplo, é uma resposta dum GPS:

$GPBWC,220516,5130.02,N,00046.34,W,213.8,T,218.0,M,0004.6,N,EGLM*11

Com um buffer de 28 caracteres a tua mensagem seria esta:

$GPBWC,220516,5130.02,N,0004

E depois não saberias em que estado está o buffer.

A implementacão ideal para isto seria:

-Activares as interrupcoes da porta série.

-Receberes os dados para um buffer.

-testar se recebeste o fim da string (se for NMEA, a string acaba com <CR><LF>)

-indicar ao programa principal que existem dados para serem consumidos.

-processar a string.

Desculpem o testamento


include <ai se te avio>

Mãe () {

}

Share this post


Link to post
Share on other sites
Silva Manuel

Obrigado a todos pelos comentário.

Daj, obrigado, o programa que sugeriu está a funcioar sem problemas, talvez o altere para funcionar com interrupts.

Bubulindo, obrigado pela sugestão do peter fleury, (vou realmente procurar na net) e também pelo tipo de estrutura do programa, embora tenha alguma dificuldade em  fazer o mesmo com forma que indicou.

 

Eu já tenho um rotina para trabalhar com interrupts que funcionava perfeitamente mas pensei usar o método de "pulling" por uma questão de temporização do sistema que estou a desenvolver. O que eu pretendo é um sistema para instalar num aeromodelo com GPS (o GPS tem uma opcão para envio de dados em formato binário que se torna mais compacto), contador de rotações, velocidade relativamente ao vento, velocidade descida/subida (só para planadores), altitude relativa,  tensão da bateria e RSSI do receptor. Estes dados serão enviados através de um sistema de rádio control com duas vias (FrSky) com um baud rate de 1200. Durante um segundo posso mandar cerca de 120 bytes.  O meu objectivo é usar o GPS para marcar a cadência  do envio de dados, dai usar o "pulling" para iniciar o processo e depois começar a enviar os restantes dados. Tenho muitas dificuldades para fazer isto em C, pois, estou a iniciar-me nesta linguagem, em assembly, fazia isto rapidamente sem qualquer problema (gosto muito de trabalhar em assembly), mas, como tenho que fazer cálculos de altitude usando a pressão atmosférica e RPM's, etc torna-se  difícil.  Eu sei que usando interrupts é muito mais fiável. Vou tentar adoptar a solução com interrupts  utilizando um timer para marcar a cadência de 1 segundo para envio de dados.

Como sistema de base, estou a pensar usar um mbed, pois, existem muitas librarias algumas para displays, etc. Gostaria de saber a vossa opinião.

Isto para mim é um "Hobby", pois, embora reformado (mas tenho que cuidar do neto), tenho alguma urgência em acabar este projecto, pois, quero iniciar outro para oferecer (e tem data marcada), portanto todas as ajudas são bem-vindas.

Obrigado.

Manuel

Share this post


Link to post
Share on other sites
Silva Manuel

Daj,

no seu código tem a seguinte expressão:

read_sig = received_byte == sig[read_sig] ? read_sig + 1 : received_byte == sig[0];  

Desculpe a minha a minha ignorância, mas não consigo perceber bem a expressão. A princípio pareceu-me o Operador Condicional "Condição? expressão1: expressão2;", mas neste caso a expressão2 não é bem uma expressão, penso que é mais uma condição.

Se me pudesse esclarecer agradecia.

Obrigado.

Manuel

Share this post


Link to post
Share on other sites
daj

Sim, o operador condicional tem essa forma mas em C uma condição é uma expressão também. Uma expressão com o operador de igualdade == é do tipo int, valendo 1 caso a igualdade seja verdadeira e 0 no caso contrário. Assim, essa linha é equivalente a:

read_sig = received_byte == sig[read_sig] ? read_sig + 1 : (received_byte == sig[0] ? 1: 0);

A comparação na expressão2 serve para verificar o caso em que o byte recebido não é o valor correcto da assinatura na posição actual mas é o primeiro byte. Um exemplo será a sequência 0xB5, 0x62, 0xB5, 0x62, 0x01, em que o terceiro byte (0xB5) é inválido como terceiro byte da assinatura mas é válido como primeiro. Neste caso read_sig passará a ter o valor 1 porque já foi lido um byte da assinatura. Na sequência 0xB5, 0xFF, 0xB5, 0x62, 0x01, o segundo byte (0xFF) não é válido como segundo byte da assinatura nem como primeiro, e read_sig será 0.

Como sistema de base, estou a pensar usar um mbed, pois, existem muitas librarias algumas para displays, etc. Gostaria de saber a vossa opinião.

Aproveito também esta mensagem para dizer que não posso ajudar neste ponto porque não tenho conhecimentos nessa área. Para dúvidas de C, ajudarei no que souber.

Share this post


Link to post
Share on other sites
bubulindo

Eu já tenho um rotina para trabalhar com interrupts que funcionava perfeitamente mas pensei usar o método de "pulling" por uma questão de temporização do sistema que estou a desenvolver. O que eu pretendo é um sistema para instalar num aeromodelo com GPS (o GPS tem uma opcão para envio de dados em formato binário que se torna mais compacto), contador de rotações, velocidade relativamente ao vento, velocidade descida/subida (só para planadores), altitude relativa,  tensão da bateria e RSSI do receptor. Estes dados serão enviados através de um sistema de rádio control com duas vias (FrSky) com um baud rate de 1200. Durante um segundo posso mandar cerca de 120 bytes.  O meu objectivo é usar o GPS para marcar a cadência  do envio de dados, dai usar o "pulling" para iniciar o processo e depois começar a enviar os restantes dados. Tenho muitas dificuldades para fazer isto em C, pois, estou a iniciar-me nesta linguagem, em assembly, fazia isto rapidamente sem qualquer problema (gosto muito de trabalhar em assembly), mas, como tenho que fazer cálculos de altitude usando a pressão atmosférica e RPM's, etc torna-se  difícil.  Eu sei que usando interrupts é muito mais fiável. Vou tentar adoptar a solução com interrupts  utilizando um timer para marcar a cadência de 1 segundo para envio de dados.

Se eu percebi bem, os dados serão enviados para o controlador, certo? Porque é que pensas em limitar o envio dos dados de 1 em 1 segundo?

Como sistema de base, estou a pensar usar um mbed, pois, existem muitas librarias algumas para displays, etc. Gostaria de saber a vossa opinião.

Isto para mim é um "Hobby", pois, embora reformado (mas tenho que cuidar do neto), tenho alguma urgência em acabar este projecto, pois, quero iniciar outro para oferecer (e tem data marcada), portanto todas as ajudas são bem-vindas.

Obrigado.

Manuel

Nunca usei uma mbed e estou mais habituado a usar AVRs. :\ É com AVR ou outro processador?


include <ai se te avio>

Mãe () {

}

Share this post


Link to post
Share on other sites
Silva Manuel

Bubulindo, obrigado pelos seus comentários.

Eu pretendo usar a cadência de um segundo para cálculo da velocidade de descida que é em metros/segundo. O cálculo da velocidade de descida, assim como os vários alarmes conforme a velocidade de descida/subida é maior ou menor estou a pensar faze-lo no sistema da base. 

Eu também (só) conheço bem os AVR e gosto bastante da sua arquitectura (mas só sei assembly) o sistema mbed é programado em C e usa um MCU ARM a 100MHz http://mbed.org/handbook/Tour e o uso do hardware é estilo Arduino. Existem presentemente muitas liberarias para o mesmo.

Cumprimentos,

Manuel

Share this post


Link to post
Share on other sites
bubulindo

E como é que essa cadência é calculada?

Podes fazer assim, mas se por algum motivo o envio de dados atrasar ou adiantar, o cálculo da velocidade pode sair errado. E claro, não podes ter nenhuma interrupcão activa no teu código, senão é garantido que vai falhar (a não ser que desactives as interrupcões enquanto envias os dados).

Mais uma vez, para mim parece-me que a melhor forma é colocares a UART a funcionar com interrupcões (se o código do Fleury for muito confuso, encontra-se mais exemplos de uarts com interrupcões na net) e teres um temporizador a criar uma interrupcão de X em X tempo para poderes calcular a velocidade de descida/subida.

Certamente que precisarás de utilizar temporizacões no programa mais à frente e é extremamente mais simples fazê-lo com um temporizador do que fiares-te na execucão ciclíca do programa. Isto para programas complexos, claro. :D


include <ai se te avio>

Mãe () {

}

Share this post


Link to post
Share on other sites
Silva Manuel

Bubulindo, mais uma vez obrigado pelos teus comentários.

A primeira rotina que fiz para ler a USART foi com interrupts e estava a funcionar correctamente, só depois, é que tentei usar o sistema "pulling". O  que penso poderá ser um problema é o facto de ter várias rotinas de interrupts a funcionar e o C não gostar muito, pois, preciso de outro interrupt por milisegundo para contar o tempo de contador de rotações e a própria contagem é feita com interrupts. No entanto vou seguir o teu conselho e tentar usar a interrupt de milisegundo para manter a cadência das várias rotinas.

Praticamente já acabei a rotina de contagem das rotações, vou montar o hardware para fazer testes e depois digo alguma coisa.

Mais uma vez obrigado.

Manuel 

Share this post


Link to post
Share on other sites
Silva Manuel

Bubulindo, afirmativo, o mbed só permite compilar pela net, e só é possível o registo se o mbed estiver ligado a uma porta USB.

Cumprimentos,

Manuel

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.