quarta-feira, 12 de abril de 2023

Usando o timer do microcontrolador EFM8BB1(linha Busy Bee).

     Olá! hoje exploraremos um pouco do microcontrolador Busy Bee EFM8(EFM8BB1), dentre suas principais características está o fato de ter um ADC de 12BITS,funciona a 25mhz interno, pinos compativel com 5V(pois são geralmente pra 3,3V), sensor de temperatura interno, 4 timers de 16 bits, comunicação UART,SPI e SMBUS/I2C.

    Este microcontrolador é totalmente compatível com alguns microcontroladores da linha CIP-51 8051(C8051), sendo praticamente um melhoramento dos projetos anteriores deste componente. Para a utilização deste microcontrolador divergem alguns registradores além do precisar de 2 comando para desligar o "watch dog", sob risco do micro ficar resetando.

    Neste exemplo vou demonstrar como acionar um pino usando como base de tempo o timer2 interno deste microcontrolador, apesar de ser um exemplo simples creio que irá ajudar quem está começando a usar este  microcontrolador com a interface do Simplicity Studio. Veja na figura a seguir. 

O código fonte :

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "si_toolchain.h"             // compiler declarations
#include <SI_EFM8BB1_Register_Enums.h>                // SFR declarations
#include <stdio.h>
#include <string.h>

#define SYSCLK       24500000          // Internal oscillator frequency in Hz

SI_SBIT(BOT1, SFR_P0, 1);                  // não usado aqui
SI_SBIT(BOT2, SFR_P0, 2);                  // não usado aqui

//leds vermelho e sinal de saida

SI_SBIT(LED_1,SFR_P1,2);


int  pisca_led = 0;
long  tempo_1s = 0;

void SiLabs_Startup (void)
{
    WDTCN = 0XDE;//             WDTE = 0 (desliga watchdog )
    WDTCN = 0xAD;//             senão colocar essa seuqencia não desliga deser colocado obrigatoriamente os 2
    PCA0MD = 0x08; //           habilita sytem clok para timer
}


void __delay_ms(uint32_t val)
{//rotina que usa ciclos de maquina - pode variar devido a interrupções
  val = 360*val;
  while(val>1)
           {
            val--;
           }
}

void __delay_us(uint8_t val)
{//rotina que usa ciclos de maquina - pode variar devido a interrupções
  while(val>1)
         {
          val--;
         }
}
void Oscillator_Init (void)
{
   HFO0CAL = 0X00; // para alta frequencia
   CLKSEL = 0x00;                     //SYSCLK: OpcDiv = 24M5Hz  // sem divisão de clock e para alta frequencia// clok interno
}
void PORT_Init(void)
{
              WDTCN = 0XDE;//             WDTE = 0 (desliga watchdog )
              WDTCN = 0xAD;//             senão colocar essa seuqencia não desliga deser colocado obrigatoriamente os 2
              CLKSEL = 0X00; // 24mhz clock interno
             // WDTCN  =  0xDE;//(desliga watchdog )
              XBR0    = 0x00;                     // habilita UART no pino P0.4(TX) and P0.5(RX)  ; logo não serão usado como como IO´s gerais
              XBR2    = 0xC0;                     // habilita crossbar e pull-ups interno desligados

              VDM0CN = 0x00;                     // Colocado porque senão da problema na gravacao da flash(já estava isso)
              RSTSRC = 0x00;                     // desativa reset de falha de oscilação.

              P0MDOUT = 0XC0;                    //somente pino 6 e 7 são saidas
              P1MDOUT = 0xFF;                     //todos saidas
              P2MDOUT = 0xFF;                     //todos saidas so tem 2 saidas

             //configura portas
              P0MDIN  = 0xF9;                     //  apenas  P0.1 e P0.2 são analogica
              P1MDIN  = 0xFF;                     //  are digital

              P0SKIP = 0x3F;      //não usado
              P1SKIP = 0xFE;
              IE_EA = 1; // liga interrupções
}

//-----------------------------------------------------------------------------
// Timer2_Init
//-----------------------------------------------------------------------------
//
// Configure Timer2 to 16-bit auto-reload and generate an interrupt at
// interval specified by <counts> using SYSCLK/48 as its time base.
//
//-----------------------------------------------------------------------------
void Timer2_Init (void)
{  //100us
   TMR2CN0 = 0x00;  /// time 2 no modo 16 bits
   TCON = 0;//não habilita timer 0  e 1
   CKCON0 = 0XFD; //todos os timer serão ligados ao sysclock e na menor divisão para timer 0 e 1(por 4)
   TMR2RLH  = 0xF6; //quando se usa o modo autocarregar puxa daqui o valor
   TMR2RLL  = 0x70;//quando se usa o modo autocarregar puxa daqui o valor
   TMR2    = 0xF670;                      // um valor para primeiro estouro
   IE_ET2     = 1;                        // enable Timer2 interrupts
   TMR2CN0_TR2 = 1;                        // start Timer2
}


//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Timer2_ISR
//-----------------------------------------------------------------------------
// This routine changes the state of the LED whenever Timer2 overflows.
//
//-----------------------------------------------------------------------------
SI_INTERRUPT(Timer2_ISR, TIMER2_IRQn)
{       // tempo de 100us
        TMR2CN0_TF2H = 0; // limpa tanto a interrupção nos bytes baixo como altos
        TMR2CN0_TF2L = 0;// clear Timer2 interrupt flag
        tempo_1s++;
}

//-----------------------------------------------------------------------------
// main() Routine
// ----------------------------------------------------------------------------
// Note: the software watchdog timer is not disabled by default in this
// example, so a long-running program will reset periodically unless
// the timer is disabled or your program periodically writes to it.
//
// Review the "Watchdog Timer" section under the part family's datasheet
// for details. To find the datasheet, select your part in the
// Simplicity Launcher and click on "Data Sheet".
//-----------------------------------------------------------------------------
int main (void)
{
  PORT_Init();
  Timer2_Init ();
  for(;;)
    {

         if(tempo_1s > 10000)
           {//pisca a cada 1s
             tempo_1s = 0;
            if(pisca_led == 0)
                {
                   pisca_led = 1;
                    LED_1 = 1;
                }
              else
                {
                   pisca_led = 0;
                    LED_1 = 0;
                }
           }
    }
}
Formatado no site http://hilite.me/  acessado em 12/04/2023.


terça-feira, 14 de fevereiro de 2023

Acionando Motor de passo a 4 fios (topologia 2 bobinas) com o PIC16F88

         Olá tudo bem! 

     Depois de um longo período sem postar nada trago um exemplo de acionamento de motor de passo que opera no modo 2 bobinas (apenas 4 fios).

    Neste exemplo foi utilizado um motor japonês STH36C1078 retirado de uma câmera de vigilância, mas pode-se utilizar qualquer outro motor de passo de 4 fios, pic 16F88, dois CI 74HC00, 8 transistores BC548, 8 diodos 1N4148, duas microswitch(tipo push botton) , um led sendo o restante composto por resistores.

     Para este exemplo pensei em algo simples como um programa que ao pressionar o botão 1 o motor gira em sentido horário e ao pressionar outro botão 2 gira em sentido anti-horário.

    Observe que a vantagem deste drive montado é que jamais ocorrerá a condição de curto, supondo que o CI esteja bom, pois o CI74HC00 faz o trabalho de sempre alternar os estado lógicos que chegam a base do transistor assim a tabela de cada drive é ‘01’- aciona a bobina num sentido (por exemplo RB0 = 0 e RB1 = 1) ,’10’ – aciona em sentido inverso, sendo que os estado ‘00’ e ‘11’ deixam as bobinas desligados, para este motor são montados 2 circuitos deste tipo(um para cada bobina).

    Deve se estar atento a sequência de ligação dos fios do motor para que ele não fique apenas “tremendo” em vez de girar. Neste projeto coloquei um resistor de baixo valor(27R) em serie com a alimentação dos transistores drives caso ocorra alguma falha este resistor funciona com fusível, porem pode se utiliza um fusível.

    Este drive pode funcionar com 5 a 12V(teoricamente pode até 30V porém não testei isso) enquanto o resto do circuito pode funciona com 5V, isso é bom pois assim não sobrecarrega o regulador de tensão que alimenta o microcontrolador.

O esquema elétrico simulado :

O código fonte:

/* Empresa cliente: BLOG MICROCONTROLADORES
 * Compilador : MPlabXC8
 * Microcontroladores compativeis : 16F88A
 * Autor: Aguivone Moretti Fógia
 * Versão: 1.0
 * Data :  14/02/2023
 * descrição : acionando motor de passo com 4 fios 
 */

#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 8000000
//#define _XTAL_FREQ 20000000
#include <xc.h>

#pragma config FOSC  = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
//#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator) -- cristal de 20mhz
#pragma config WDTE  = OFF       // Watchdog Timer Enable bit (WDT habilitado)
#pragma config PWRTE = OFF       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP   = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD   = OFF         // Data EE Memory Code Protection bit (proteção da eeprom)
#pragma config CP    = OFF         // Flash Program Memory Code Protection bit (proteção do codigo

///////////////defines utilizados///////////////////////////////////////////////////////////////////////////////////////////

/*************************** configuração dos pinos **************************************************/
////////PINOS DO MOTOR ///////////////
//a bobina 1 é o fio amarelo e vermelho --->>  bobina 2 é o fio laranja e marrom
//no motor fisico a sequencia  para o modo 1 passo é 

/*
 Passo/cor
 * 1   amarelo(+)    vermelho(-)     laranja(desl)   marrom(desl)
 * 2   amarelo(desl) vermelho(desl)  laranja(+)      marrom(-)
 * 3   amarelo(-)    vermelho(+)     laranja(desl)   marrom(desl)
 * 4   amarelo(desl) vermelho(desl)  laranja(-)      marrom(+)
 * para motor desligado = todos os pinos em zero ou em 1
 
 
 */
 #define A_0              RB0   //bobina A  //deve ser colocado corretamente para girar
 #define A_1              RB1               

 #define B_0              RB2  //bobina B   
 #define B_1              RB3                 

 #define BOT_H            RA0  
 #define BOT_ANTI         RA1  
///////// PINO DE SINALIZAÇÃO //////////////
#define  LED_STATUS       RB4

void Desligar_motor(void)
{
   A_0 = 0;
   A_1 = 0;
   B_0 = 0;
   B_1 = 0;
}
void tempo_pulso(long tempo)
{//pra pode usar tempos maiores
    while(tempo>0)
    {
     __delay_ms(1);
     tempo--;
    }
}

void Girar_motor_horario(long tempo,int num_giros)
{//o tempo ira definir a velocidade de giro
    while(num_giros>0)
    {
        A_0 = 1;
        A_1 = 0;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 1;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 1;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 0;
        B_1 = 1;
        tempo_pulso(tempo);
        num_giros--;
    }
}

}
void Girar_motor_anti_hor(long tempo,int num_giros)
{//o tempo ira definir a velocidade de giro
    while(num_giros>0)
    {
        A_0 = 0;
        A_1 = 0;
        B_0 = 0;
        B_1 = 1;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 1;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 1;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 1;
        A_1 = 0;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        num_giros--;
    }
}

void main(void)
{
    TRISB = 0XC0;//configura portB
    TRISA = 0XFF;//configura portA
    PORTA = 0;
    PORTB = 0;//deixa pino tx em nivel alto
    OPTION_REG = 0X80; // pull up desabilitados e prescaler para timer 0 - desabilita watchdog
    INTCON = 0;//desabilita todas as interrupções 
    CMCON = 0X07;//desliga comparadores 
    OSCTUNE = 0X1F;  // oscilador vel maxima
    OSCCON = 0XFE ; // oscilador interno com frequencia de 8mhz
    ANSEL = 0X00; //portas digitais desligado. 
    GIE = 0;   
   for(;;)
     { 
       
       if(BOT_H == 0)
       {
          Girar_motor_horario(5,10);// tempo de acionamento da bobina é de 1ms e dara 10 giros 
          LED_STATUS = 1;
       }
       else
       {
            if(BOT_ANTI == 0)
            {
               Girar_motor_anti_hor(5,10);
               LED_STATUS = 1;
            }
            else
            {
               Desligar_motor();
               LED_STATUS = 0;
            }
     }
     }//loop infinito
}

Formatado no site: http://hilite.me/   14/02/2023.

Vídeo de demonstração: 



sexta-feira, 23 de setembro de 2022

Voltímetro para corrente alternada - Voltimetro AC com PIC

 Olá, hoje postarei um voltímetro para ler sinais AC usando o microcontrolador PIC16F88, no qual analisa um sinal de corrente alternada no pino RA0 e retorna o valor lido em milivolts na porta serial, atente que na simulação e no protótipo real o valor na função de configuração da serial é diferente.

    Este projeto é útil quando se precisa ler o valor de pico da tensão, com alguma pequenas modificações é possível ter uma tensão média(tipo do multímetro) ou o valor de pico (tensão RMS). Para simulação foi usado apenas um gerador de sinal, um osciloscopio e um terminal serial configurado para (19200 de baud rate), no pic a frequência de clock é de 8 mhz (pois está usando o oscilador interno). Este projeto funcionou bem na protoboard e na simulação. Favor clicar na imagem para ver melhor os detalhes da tensão.


O código fonte :
/*
 * Empresa cliente: exemplo_blog
 * Compilador : MPlabXC8
 * Microcontroladores compativeis : 16F88A
 * Autor: Aguivone Moretti Fógia
 * Versão: 1
 * Data :  23_09_2022   -> ler tensao AC
 */

#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 8000000
#include <xc.h>

/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

#pragma config FOSC  = INTOSCIO  // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
//#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator) -- cristal de 20mhz
#pragma config WDTE  = OFF       // Watchdog Timer Enable bit (WDT habilitado)
#pragma config PWRTE = OFF       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP   = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD   = OFF         // Data EE Memory Code Protection bit (proteção da eeprom)
#pragma config CP    = OFF         // Flash Program Memory Code Protection bit (proteção do codigo)


#define  TAM_MAX   10//tamanho maximo do vetor de amostragem(numero de pontos)
#define  RANGE_ADC 10//variação maxima permitida (filtro de ruido)

#define LED_STATUS   RB0
#define  TEMPO_ENVIO  5000//(aprox. 0,5s)tempo para enviar os dados(em tempo de rotina))

int vetor[TAM_MAX+1];  //armazena as amostra temporaria
char flag_libera=0;
char tempo_media = 0;
int tempo_de_envio = 0;
int tensao_pico=0;


/////////////////////////////////funçoes usadas pela uart /////////////////////////////////////////////////////////////////

/*datasheet pag 101 - baudrate para 8mhz  - high speed
 baud rate desejado    -  baud rate real - faixa de erro - SPBRG
 2400  2404   0,16  207
 9600  9615   0,16  51
19200  19231  0,16  25
28800  29412  2,12  16
38400  38462  0,16  12
57600  55556  3,55  8
 */
void inicializa_RS232(long velocidade,int modo,char pega_ref)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    //verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    long valor;
        if(modo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//8bits/transmissão habilitado/modo assincrono/highspeed
         valor =(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate -> 10_06_2022
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;////8bits/transmissão habilitado/modo assincrono/lowspeed
         valor =(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    if(pega_ref == 0)
    {//valor calculado
       SPBRG =(int)valor; // veja a pagina 101 do datasheet já tem calculado para 8MHZ
    }
    else
    {//valor colocado diretamente
      SPBRG =(int)velocidade; //coloca valor direto baseado no data sheet  
    }
    RCIE = 1;//habilita interrupção de recepção
    PEIE =1;
    TXEN = 1; // habilita o modulo de transmisão
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
///////////conversor adc //////////////////////////
// 0 - lê sensor de luz  1 - lê sensor magnetico  
int ler_adc_un(void)  //(int sele)
{
        int lido_adc;
        GIE = 0;//para não travar com outra interrupção     
        ADCON0 = 0XC5;//pede nova conversão //AN0
        while (ADCON0 == 0XC5); //espera finalizar leitura
        GIE = 1;
        lido_adc = (ADRESH << 8) | ADRESL; // XXXXXXXXLL   
        return(lido_adc);
}
void interrupt interrupcao(void)//vetor de interrupção
 {
    char teste;
    if(RCIF == 1)
    {//interrupção de recepção de dados
        //lembrar de por um timeout pra não ficar travado esperando dados
        RCIF = 0;//  limpa flag de interrupção de recepção
        teste = RCREG;
    }
}
void long_to_char(int valor)
{
    char valores[] = {'0','0','0','0'};
    while(valor>=1000)
       {
           valor = valor - 1000;
           valores[0]++;
       }
    while(valor>=100)
       {
           valor = valor - 100;
           valores[1]++;
       } 
    while(valor>=10)
       {
           valor = valor - 10;
           valores[2]++;
       } 
    while(valor>=1)
       {
           valor = valor - 1;
           valores[3]++;
       } 
    for(int indice = 0;indice < 4;indice++)
       {
           while(TRMT==0);//espera enviar caracter, esvaziando o shift register , usando o trmt é mais garantido a entrega 
           TXREG = valores[indice];//no datasheet diz que ao carregar o TXREG limpa o bit TXIF           
           while(TRMT==0);
       }
       TXREG ='\r';//retorna paar inicio de linha          
       while(TRMT==0);
       TXREG ='\n';//pula linha          
       while(TRMT==0);
}
void rotaciona_vetor()
{ // apenas rotciona e descarta o valor que estava em vetor[0]
    int aux=0;
    while(aux<TAM_MAX)
    {
        vetor[aux] = vetor[aux+1];
        aux++;
    }
}
void ordena_vetor()
{//coloca em ordem crescente onde o maior valor estará em vetor[tam_max]
    int aux=0;
    int aux2=0;
    int houve_troca = 1;
    while(houve_troca == 1)
    { ///faz isso enquanto houver troca de posição
        houve_troca = 0;
        aux=0;
        while(aux<TAM_MAX)
        {
            if(vetor[aux] > vetor[aux+1])
            {
               aux2 = vetor[aux+1];
               vetor[aux + 1] = vetor[aux];
               vetor[aux] = aux2;
               houve_troca = 1;
            }        
            aux++;
        }
    }
}
int analisa_sinal(void)
{ //analisa os 4 valores mais altos
    int valor_pico = 1050;//valor impossivel -> só pra indicar erro na leitura
    ordena_vetor();// coloca em ordem crescente
    ////verifica se há valores discrepantes//// erro ou ruido
    //veja que qualquer coisa dessas possibilidade é interpretado como zero
    
    if((vetor[TAM_MAX]-vetor[TAM_MAX-1])< RANGE_ADC) //se a diferença não for maior que o range
    {//senão houver valor discrepante está ok
        if((vetor[TAM_MAX-1]-vetor[TAM_MAX-2])< RANGE_ADC) //se a diferença não for maior que o range
            {//valida os 3 ultimos
              valor_pico = (vetor[TAM_MAX]+vetor[TAM_MAX-1]+vetor[TAM_MAX-2])/3;
            }
             
    }
    else
    {
        if((vetor[TAM_MAX-1]-vetor[TAM_MAX-2])< RANGE_ADC) //se a diferença não for maior que o range
            {//descarta o maior valor
               if((vetor[TAM_MAX-2]-vetor[TAM_MAX-3])< RANGE_ADC) //se a diferença não for maior que o range
                    {//valida 
                      valor_pico = (vetor[TAM_MAX-1]+vetor[TAM_MAX-2]+vetor[TAM_MAX-3])/3;  
                    }
            }     
    } 
    return(valor_pico);
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////çlj
void main(void)
{
    TRISB = 0X04;//configura portB como saida ,  RB2 é entrada pois é rx 232 e B4 não será usado
    TRISA = 0XFF;//configura portA como entrada .só RA4 e RA3 como saida 
    PORTA = 0;
    PORTB = 0;
    OPTION_REG = 0X80; // pull up desabilitados e prescaler para timer 0 - desabilita watchdog
    INTCON = 0;//desabilita todas as interrupções 
    CMCON = 0X07;//desliga comparadores 
    OSCTUNE = 0X1F;  // oscilador vel maxima
    OSCCON = 0XFE ; // oscilador interno com frequencia de 8mhz
    ///  OSCCON = 0X7C ; // oscilador cristal externo 20mhz
    /////ADC/////////////
    ANSEL = 0X01; //portas digitais e AN0 .
    ADCON0 = 0XC1;//ligado ao oscilador RC  - AN0 selecionado  e modulo de conversão ligado 
    ADCON1 = 0X80;//justificado a direita(assim os 2bits mais significativos fica no primeiro byte) , ref ligado ao vdd
   // ADCON1 = 0XC0;//justificado a direita(assim os 2bits mais significativos fica no primeiro byte) , ref ligado ao vdd   -> clock  dividido por 2
    /////////////////
    PIE1bits.ADIE = 0;// desliga interrupção do conversor ADC     
    //19200 -> modo de alta velocidade -> creio que o clock não está muito bem calibrado pois não está dando muito certo   
    //valores validos entre spbrg 27 e 30 -> para 19200 logo valores mais adequados 27 a 29 = mas 28 ficou melhor
    inicializa_RS232(29,1,1);//passa valor direto do registrador na placa fisica deve ser 28 ou 29
    inicializa_RS232(25,1,1);//passa valor direto do registrador para simular no proteus deve ser 25 
    GIE = 1;            
    for(;;) 
    {  //  atualiza os vetores 
         __delay_us(100); //para garantir uma leitura estavel 0,4ms
        vetor[TAM_MAX] = ler_adc_un();        
        if((flag_libera == 1))
        {
             if(vetor[TAM_MAX]<vetor[0])//localizou a regiao de pico  de tensão
            {
                if(analisa_sinal() < 1025)  //busca valor de pico valido
                { 
                    tensao_pico = analisa_sinal(); //valor instantaneo
                } 
              flag_libera=0;
            }   
        }
        else
        {//flag de liberação desligado
           if(vetor[TAM_MAX] > vetor[0])//localizou o inicio de subida
            {
               flag_libera=1;//vasculha o ponto de subida
            }
        }            
        rotaciona_vetor();
        if(tempo_de_envio >= TEMPO_ENVIO)
                 {
                    int aux=0;
                    aux = (tensao_pico*49)/10;
                    long_to_char(aux);   // valor medio 
                    LED_STATUS = ~LED_STATUS;//pisca a cada envio
                    tempo_de_envio = 0;
                 }        
        tempo_de_envio++;       
    }
}

Formatado no site http://hilite.me/ no dia 23/09/2022.


sexta-feira, 22 de julho de 2022

Comunicação serial com o microcontrolador C8051F832

         Exemplo de como fazer uma comunicação serial usando o microcontrolador C8051F832 de 16 pinos. Basta colocar os caractere precedido por  "*" para indicar fim de recepção.

/*
 * 
 * Compilador : Simplicity studio
 * Microcontroladores compativeis : C8051F832
 * Autor: Aguivone Moretti Fógia
 * Versão: 1.0
 * Data :  22_07_2022           ->  inicio de teste de comunicação serial
 * Descrição:
 * teste de comunicação serial
 * Basta envia um dado e depois o caracter "*" para indicar fim de recepção
 * exemplo:  teste*  ira ter como retorno "TESTE"
 */

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "si_toolchain.h"
#include <SI_C8051F800_Defs.h>            // SFR
#include <stdio.h>
#include <string.h>

#define SYSCLK           24500000      // SYSCLK em hertz usando clock interno
#define BAUDRATE           19200      // Baud rate em bps

//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------

void SYSCLK_Init (void);
void UART0_Init (void);
void PORT_Init (void);
SI_INTERRUPT_PROTO(UART0_ISR, INTERRUPT_UART0);


//-----------------------------------------------------------------------------
// variaveis globais
//-----------------------------------------------------------------------------

#define UART_TAM_MAX 64
uint8_t UART_Buffer[UART_TAM_MAX] = {0};
uint8_t UART_buffer_tamanho = 0;
uint8_t UART_primeira_entrada = 0;
uint8_t UART_primeira_saida = 0;
uint8_t Enviar_dados =1;
static uint8_t Caracter_temporario;

//-----------------------------------------------------------------------------
// SiLabs_Startup()
// ----------------------------------------------------------------------------
// Essa rotina roda sempre primeiro que o rotina main
//-----------------------------------------------------------------------------
void SiLabs_Startup (void)
{
   PCA0MD &= ~0x40;                    // WDTE = 0 (desliga watchdog )
}

//-----------------------------------------------------------------------------
//Rotina principal
//-----------------------------------------------------------------------------

void main (void)
{

   PORT_Init();                        // inicializa portas
   SYSCLK_Init ();                     // inicializa oscilador
   UART0_Init();                       // inicializa UART
   IE_EA = 1;                          // habilita interrupções
   UART_Buffer[0] ='D';
   UART_Buffer[1] ='I';
   UART_Buffer[2] ='G';
   UART_Buffer[3] ='I';
   UART_Buffer[4] ='T';
   UART_Buffer[5] ='E';
   UART_Buffer[6] =' ';
   UART_Buffer[7] ='A';
   UART_Buffer[8] ='L';
   UART_Buffer[9] ='G';
   UART_Buffer[10] ='O';
   UART_Buffer[11] ='!';
   UART_buffer_tamanho = 12;             // atualiza tamanho do buffer
   UART_primeira_entrada = 12;
   Enviar_dados = 0;                 // limpa flag
   SCON0_TI = 1;                 // indica pra trasnmitir pois tem dados
   while(1)
   {
      // se houver dados recebido será enviado
      if((Enviar_dados == 1) && (UART_buffer_tamanho != 0) && (Caracter_temporario == '*'))//tx pronto /buffer cheio / finalizador de pacote
      {
         Enviar_dados = 0;                 // limpa flag
         SCON0_TI = 1;                 // indica pra trasnmitir pois tem dados
      }

   }
}

//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the Crossbar and GPIO ports.
//
// P0.4   digital   push-pull    UART TX = como saida
// P0.5   digital   open-drain   UART RX = como entrada
//
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
   P0MDOUT |= 0x10;                    // Enable UTX as push-pull output
   XBR0    = 0x01;                     // Enable UART on P0.4(TX) and P0.5(RX)
   XBR1    = 0x40;                     // Enable crossbar and weak pull-ups
}

//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This routine initializes the system clock to use the internal precision
// oscillator at its maximum frequency and enables the missing clock
// detector.
//
//-----------------------------------------------------------------------------
void SYSCLK_Init (void)
{
   OSCICN |= 0x83;                     // Enable the precision internal osc.

   RSTSRC = 0x06;                      // habilita detector de falta de clock e
                                       // deixa monitor de VDD habilitado.

   CLKSEL = 0x00;                      // seleciona a precisão interna.
                                       // clock sem divisão
}

//-----------------------------------------------------------------------------
// UART0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the UART0 using Timer1, for <BAUDRATE> and 8-N-1.
//-----------------------------------------------------------------------------
void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate
                                       //        level of STOP bit is ignored
                                       //        RX enabled
                                       //        ninth bits are zeros
                                       //        clear RI0 and TI0 bits
   #if (SYSCLK/BAUDRATE/2/256 < 1)
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   #elif (SYSCLK/BAUDRATE/2/256 < 4)
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01
      CKCON |=  0x01;
   #elif (SYSCLK/BAUDRATE/2/256 < 12)
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   #else
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   #endif

   TL1 = TH1;                          // Initialize Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;
   TCON_TR1 = 1;                       // START Timer1
   Enviar_dados = 1;                       // Flag showing that UART can transmit
   IP |= 0x10;                         // Make UART high priority
   IE_ES0 = 1;                         // Enable UART0 interrupts

   SCON0_TI = 1;
}

//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// UART0_Interrupt
//-----------------------------------------------------------------------------
//
// This routine is invoked whenever a character is entered or displayed on the
// Hyperterminal.
//
//-----------------------------------------------------------------------------
SI_INTERRUPT(UART0_ISR, UART0_IRQn)
{
   if (SCON0_RI == 1)
   {
      if( UART_buffer_tamanho == 0)
        {// tem dados
         UART_primeira_entrada = 0;
        }

         SCON0_RI = 0;                      // limpa flag de interrupção de recepção

         Caracter_temporario = SBUF0;                      // Lê o caracter que está no buffer

      if (UART_buffer_tamanho < UART_TAM_MAX)
      {
         UART_Buffer[UART_primeira_entrada] = Caracter_temporario; // coloca no buffer para transmitir

         UART_buffer_tamanho++;             // atualiza tamanho do buffer

         UART_primeira_entrada++;             // atualiza contagem
      }
   }

   if (SCON0_TI == 1)                    // verifica se houve interrupção de transmissão
   {
     SCON0_TI = 0;                     // limpa flag

      if (UART_buffer_tamanho != 0)         // se tiver dados a enviar envia
      {
         //tem um pacote valido
         if ( UART_buffer_tamanho == UART_primeira_entrada ) {
              UART_primeira_saida = 0;  }

         // armazena informação em Caracter_temporario
         Caracter_temporario = UART_Buffer[UART_primeira_saida];

         if ((Caracter_temporario >= 0x61) && (Caracter_temporario <= 0x7A)) { // se não tiver em caixa alta(maiuscula) passa para caixa alta.
            Caracter_temporario -= 32; }

         SBUF0 = Caracter_temporario;                   // coloca no registrador de transmissão de dados

         UART_primeira_saida++;            // atualiza contagem

         UART_buffer_tamanho--;             // decrementa o tamanho

      }
      else
      {
         UART_buffer_tamanho = 0;           // zera contagem
         Enviar_dados = 1;                   // indica que dados foram enviado
      }
   }
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

Formato no site  http://hilite.me/  no dia 22/07/2022.

quinta-feira, 14 de outubro de 2021

Acessando a memoria flash(ROM) do C8051F380 / C8051F382 usando o Simplicity Studio .

ACESSANDO A MEMÓRIA ROM(flash) DO C8051F380 / C8051F382   


     Olá, bom dia atualmente a maioria dos microcontroladores tem uma memória rom interna. Porém ao trabalhar com o C8051F382 percebi que não tem! isso me causou um certo desconforto. buscando por uma solução observei que é possível usar a memoria flash como se fosse uma memoria ROM.

   Porém o uso da flash apresenta algumas características relevantes, como o fato de só poder apagar blocos de memorias(512 bytes por vez) em vez de uma só posição, outro problema é que para escrever numa posição obrigatoriamente vc deve apagar aquele bloco de memoria, ou seja, supondo que eu queira escrever na posição 1024, precisarei apagar o bloco de memoria que vai de 512 a 1024, o que pode ser desastroso se não avaliar bem onde se vai mexer pois corre o risco de apagar trechos de memorias que fazem parte do programa principal, uma recomendação e dar uma olhada nos arquivos ".list" ,gerado após compilar o código fonte, para ver como esta a localização da áreas usadas.

     O motivo de obrigatoriamente ter que apagar a memoria flash antes de escrever um novo caracteres   é devido ao fato de que quando se trabalha na flash para escrever só aceita escrever "0" nas posições, isso se deve a arquitetura como foi projetado, descobri isso fazendo alguns testes aqui, ou seja supondo que eu queira escrever em hexadecimal o valor  "0X11" numa posição, e então eu decida escrever o valor "0X08" , o esperado era ler "0X08" porem isso não acontece e é lido "0X00" ou seja  onde é zero ele coloca mas onde é 1 não é colocado, descobri isso num código em assembly dai com esse entendimento apliquei ao código que eu fiz em C.

      Veja que é possível depois de apagado o bloco da memoria ir escrevendo nas posições, porém se precisar alterar valores já gravados, terá que apagar o bloco de memoria de 512 bytes, apesar de ser uma característica de alguns microcontroladores da Silicon Labs, pode-se recorrer também para o uso de uma memoria externa .

        Uma vez explicado os risco e a limitação de hardware podemos agora passar para o código fonte. Neste código são utilizados 2 leds, um para indicar processamento e outro pra indicar que houve uma gravação num trecho de memória, assim quando ligar pela primeira vez o led não acende e a partir da segunda vez ficara acesso indicando uma leitura valida na memória, apesar do código ser simples é uma mão na roda pra que está passando por este problema. Aqui está usando o PORT P0.

O código fonte :

// Target:         C8051F382
// Tool chain:     Simplicity Studio / Keil C51 9.51
// Command Line:   None
// Autor: Aguivone Moretti Fogia
// Data: 14/10/2021
//
//exemplo de como usar a memoria flash como uma memoria ROM, apesar de não ser muito bom essa ideia pois
//caso ocorra um ruido ele pode alterar outras areas de memoria.
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_C8051F380_Register_Enums.h>
#include "F380_FlashPrimitives.h"
#include "F380_FlashUtils.h"

//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------

#define SYSCLK       12000000          // Internal oscillator frequency in Hz

//leds vermelho e sinal de saida
SI_SBIT(LED_gravado, SFR_P0, 0);                      // LED='0' Ligado
SI_SBIT(LED_pisca, SFR_P0, 1);                      // LED='1' Desligado


//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
SI_INTERRUPT_PROTO(PCA0_ISR, PCA0_IRQn);

void OSCILLATOR_Init (void);
void PORT_Init (void);
//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup (void)
{
   PCA0MD = 0x00;                      // Disable watchdog timer
}

//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------

void main (void)
{
  uint16_t LOOP;
  uint16_t LOOP_max =65000;
   PORT_Init ();                       // Initialize crossbar and GPIO
   OSCILLATOR_Init ();                 // Initialize oscillator
   LOOP =0;
   IE_EA = 0;
   if(FLASH_ByteRead(0x5FFE)== 0x12)  //olhe o datasheet para escolher um local de memoria existente,se for inexistente ele pode travar.
         {
            LED_gravado=0; //liga led indicando que posicão da memoria foi gravada
         }
     else
         {
            LED_gravado=1; //desliga led
            FLASH_PageErase(0x5FFE);//apaga bloco de 512bytes por vez (setando tudo para 1)
            // escreve valor na mmemoria - sempre deve apagar a memoria pois ele so escreve zero
            //é uma limitação do hardware.
            FLASH_ByteWrite(0x5FFE, 0x12); //grava 0X12H  na memoria
            //na proxima vez que ligar já busca na memoria este valor
         }

   while (1)
   {//pisca led
     LOOP++;
         if(LOOP>LOOP_max)
         {
             LED_pisca=0; //liga led (pois esta ligado ao vcc)
              LOOP=0;
         }
         if(LOOP ==(LOOP_max/2))
             {
             LED_pisca=1; //desliga led
             }
   }
}

//-----------------------------------------------------------------------------
// OSCILLATOR_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function initializes the system clock to use the internal oscillator
// at 12 MHz.
//
//-----------------------------------------------------------------------------
void OSCILLATOR_Init (void)
{
   OSCICN = 0x83;                      // Set internal oscillator to run  (desabilitou osc interno)
                                       // at its maximum frequency(pois setou os 2 ultimos bit)
   CLKSEL = 0x00;                      // Select the internal osc. as (SYSCLK/4)
                                       // the SYSCLK source
}

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function configures the crossbar and GPIO ports.
//
// P0.0   digital   open-drain     PCA0 CEX0
// P0.1   digital   push-pull
//
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
      OSCICN  = 0X81;                     // IHFOsc Enable, OpcDiv:3MHz
      CLKMUL  = 0XC0;                     // talvez nem precise neste microcontrolador -> habilita  e inicializa pll
      CLKSEL  = 0X03;                     // SYSCLK derived from the Internal High-Frequency Oscillator.
      PFE0CN  = 0X20;                     //(para High Speed) Prefetch Engine Control,otimiza tempo de execução lendo 2 bytes de execução por vez
      /* FLSCL.FOSE(7) = 0 for < 10MHz and 1 for SYSCLK > 10MHz
      FLSCL.FLRT(4) = 0 for < 25MHz and 1 for SYSCLK > 25MHz */
      FLSCL   = 0x90;
      CKCON   = 0x30;
      SPI0CN  = 0x02;                     //Select Mode 3-Wire pra liberar pino NSS

      P0MDIN  = 0xFF;                     //  are digital
      P1MDIN  = 0xFF;                     //  are digital
      P2MDIN  = 0xFF;                     //  are digital
      P3MDIN  = 0xFF;                     //  are digital
      P4MDIN  = 0xFF;                     //  are digital

      P0MDOUT = 0x10;                     // enable Switches as open-drain
      P1MDOUT = 0x00;                     // enable Switches as open-drain
      P2MDOUT = 0x00;                     // enable Switches as open-drain
      P3MDOUT = 0x00;                     // enable Switches as open-drain
      P4MDOUT = 0x04;                     // enable Switches as open-drain

      P0SKIP = 0xCF;                      // TXeRX precisa do Skip pra funcionar
      P1SKIP = 0xFF;
      P2SKIP = 0xFF;
      P3SKIP = 0xCF;

      XBR0   = 0x01;                     // CP1AE,CP1E,CP0AE,CP0E,SYSCKE,SMB0E,SPI0E,(UART0E)
      XBR2   = 0x00;                     // RESRV,RSRV,RESRV,RESRV,RESRV,RESRV,SMB1E, UART1E
      XBR1   = 0xC2;                     // WEAKPUD,(XBARE),T1E,T0E,ECIE,(PCA0ME):000:Nenhum,001:CEX0,010:CEX0eCEX1,...
      // VDM0CN -> monitor de tensão de reset
      VDM0CN = 0x80;                     //Colocado porque senão da problema na gravacao da flash(já estava isso)

       P4 = 0        ;  //portas de p4 desligadas
}




//-----------------------------------------------------------------------------
// End Of File

formatado por http://hilite.me/  14/10/2021