terça-feira, 17 de junho de 2025

Lendo o ADC do STM32 na IDE arduino

Olá, neste exemplo vou demonstrar como ler 2 entradas analógicas e imprimir pela serial do microcontrolador STM32F103C6T6 . O pino PA10 é o RX (ligado ao TX do PC) e o pino PA9 o TX (ligado ao RX do PC).Veja que o código é bem simples:


#define LED    PC13

  //#define BOTAO  PA7  não sera usado
  #define A0  PA0
  #define A1  PA1

 bool status_led=0;

void setup() {
Serial.begin(9600);
  pinMode(BOTAO, INPUT);  // botao
  pinMode(LED, OUTPUT);  // led de status
  pinMode(A0, INPUT);  // configura entradas analogicas
  pinMode(A1, INPUT);
}

void loop() {
  // variavei para ler AN0 e AN1
  int canal0 = analogRead(A0);
  int canal1 = analogRead(A1);
  // imprime os valores
  Serial.print("canal_A0:");
  Serial.print(canal0);
  Serial.println(",");
  Serial.print("canal_A1:");
  Serial.println(canal1);

    if(status_led == 0) // pisca led
    {
        digitalWrite(LED, HIGH);
        status_led = 1;
    }
    else
    {
        digitalWrite(LED, LOW);
        status_led = 0;
    }  
  delay(300);

}

segunda-feira, 16 de junho de 2025

Comunicação serial 232 modo DMA usando o CUBE IDE no STM32F103C6T6

     Olá, veja a demonstração como usar o recurso DMA do microcontrolador da STM32, pois este recurso permite que a CPU do microcontrolador não pare de funcionar  a cada recepção de caracteres que chegam pela serial. Vale lembrar que também é possível configurar este recuso para transmissão, ou seja pode se colocar um vetor(array) para transmissão e então habilitar (ou não) para que ao finalizar gere uma interrupção de envio de todos os dados. aqui será usado a interface gráfica do CUBE IDE.

    Aqui estou usando o microcontrolador STM32F103C6T6 da placa de desenvolvimento blue pill.

configurando o CUBE IDE :

    Basta seguir as imagens para configurar corretamente. Na primeira imagem da aba "PINOUT & CONFIGURATION -> NVIC Setting"  deve estar habilitado as 2 interrupção de DMA e global, pois uma e para coletar os caractere e a outra é acionada quando o DMA atingir o numero de caracteres configurado, observe que na interface gráfica os pinos configurados são destacados.

    Na aba DMA Settings habilita USART_RX, mas se quiser habilitar pra transmissão use o USART_TX

    Na aba Parameter Settings configure a porta serial. 

    Neste exemplo usei o cristal externo na aba RCC, porem se quiser usar o clock interno fique a vontade, pois a propria IDE faz os ajuste automaticamente neste parâmetros, quando se seta na aba "CLOCK CONFIGURATION" .

Pronto ! agora é codificar

O código fonte :


    Pra quem usa o CUBE IDE sabe que ele irá gerar automaticamente o código da estrutura "main.c" quando se salva as configurações portanto é necessário acrescentar apenas algumas variáveis e as chamadas de interrupções, neste caso utilizei a de recepção completa(HAL_UART_RxCpltCallback) mas existe a opção de avaliar quando se chega metade dos dados(HAL_UART_RxHalfCpltCallback), caso precise validar um pacote grande pode ser util. 
    Na rotina principal não precisaria ter nada, porém coloquei uma variável para demonstrar caso seja necessário fazer alguma ação depois que recebe o vetor com os dados recebidos.

#include "main.h"

#include <stdio.h>

UART_HandleTypeDef huart1;

DMA_HandleTypeDef hdma_usart1_rx;


/* USER CODE BEGIN PV */

/* USER CODE END PV */


/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

static void MX_GPIO_Init(void);

static void MX_DMA_Init(void);

static void MX_USART1_UART_Init(void);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */


/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

#define TX_BUFFER_SIZE 128 // Tamanho do buffer de transmissão


uint8_t RxData[20]; //20 bytes de recepção

uint8_t tem_dados = '0';


char txBuffer[TX_BUFFER_SIZE]; // Buffer para enviar mensagens de debug/resposta


void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)

{ //chegou a metade dos dados pode ser util em alguma aplicação

//HAL_UART_Transmit(&huart1,RxData, 10, 50); //imprime a metade coletada

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{ //chegou a quantidade de dados escolhidos

HAL_UART_Receive_DMA(&huart1,RxData,10); // solicita novamente mais 10 bytes

tem_dados = '1';

HAL_UART_Transmit(&huart1,RxData, 10, 50);

int apaga=0;

while(apaga<20)//limpa pacote

{

RxData[apaga] =' ';

apaga++;

}


}


/* USER CODE END 0 */


/**

* @brief The application entry point.

* @retval int

*/

int main(void)

{


/* USER CODE BEGIN 1 */

/* USER CODE END 1 */


/* MCU Configuration--------------------------------------------------------*/


/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();


/* USER CODE BEGIN Init */

/* USER CODE END Init */


/* Configure the system clock */

SystemClock_Config();


/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */


/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_USART1_UART_Init();

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

HAL_UART_Receive_DMA(&huart1,RxData,10);

/* Infinite loop */

/* USER CODE BEGIN WHILE */

/* USER CODE END WHILE */

HAL_UART_Transmit(&huart1, (uint8_t*)"Sistema Pronto. Aguardando dados...\n\r", strlen("Sistema Pronto. Aguardando dados...\n\r"), HAL_MAX_DELAY);

for(;;)

{

if(tem_dados == '1')

{

tem_dados = '0';

HAL_UART_Transmit(&huart1, (uint8_t*)"chegou dados!", strlen("chegou dados!"), HAL_MAX_DELAY);

}

}

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */

}


/**

* @brief System Clock Configuration

* @retval None

*/

void SystemClock_Config(void)

{

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


/** Initializes the RCC Oscillators according to the specified parameters

* in the RCC_OscInitTypeDef structure.

*/

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

RCC_OscInitStruct.HSEState = RCC_HSE_ON;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

{

Error_Handler();

}


/** Initializes the CPU, AHB and APB buses clocks

*/

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

{

Error_Handler();

}

}


/**

* @brief USART1 Initialization Function

* @param None

* @retval None

*/

static void MX_USART1_UART_Init(void)

{


/* USER CODE BEGIN USART1_Init 0 */

/* USER CODE END USART1_Init 0 */


/* USER CODE BEGIN USART1_Init 1 */

/* USER CODE END USART1_Init 1 */

huart1.Instance = USART1;

huart1.Init.BaudRate = 9600;

huart1.Init.WordLength = UART_WORDLENGTH_8B;

huart1.Init.StopBits = UART_STOPBITS_1;

huart1.Init.Parity = UART_PARITY_NONE;

huart1.Init.Mode = UART_MODE_TX_RX;

huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart1.Init.OverSampling = UART_OVERSAMPLING_16;

if (HAL_UART_Init(&huart1) != HAL_OK)

{

Error_Handler();

}

/* USER CODE BEGIN USART1_Init 2 */

/* USER CODE END USART1_Init 2 */


}


/**

* Enable DMA controller clock

*/

static void MX_DMA_Init(void)

{


/* DMA controller clock enable */

__HAL_RCC_DMA1_CLK_ENABLE();


/* DMA interrupt init */

/* DMA1_Channel5_IRQn interrupt configuration */

HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);


}


/**

* @brief GPIO Initialization Function

* @param None

* @retval None

*/

static void MX_GPIO_Init(void)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

/* USER CODE BEGIN MX_GPIO_Init_1 */

/* USER CODE END MX_GPIO_Init_1 */


/* GPIO Ports Clock Enable */

__HAL_RCC_GPIOC_CLK_ENABLE();

__HAL_RCC_GPIOD_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();


/*Configure GPIO pin Output Level */

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);


/*Configure GPIO pin : LED_Pin */

GPIO_InitStruct.Pin = LED_Pin;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);


/* USER CODE BEGIN MX_GPIO_Init_2 */

/* USER CODE END MX_GPIO_Init_2 */

}


/* USER CODE BEGIN 4 */

/* USER CODE END 4 */


/**

* @brief This function is executed in case of error occurrence.

* @retval None

*/

void Error_Handler(void)

{

/* USER CODE BEGIN Error_Handler_Debug */

/* USER CODE END Error_Handler_Debug */

}


#ifdef USE_FULL_ASSERT

/**

* @brief Reports the name of the source file and the source line number

* where the assert_param error has occurred.

* @param file: pointer to the source file name

* @param line: assert_param error line source number

* @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/* USER CODE BEGIN 6 */

/* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */


Obrigado pela atenção! e até a próxima postagem. Data: 16/06/2025         








           


terça-feira, 3 de junho de 2025

Exemplo de código com comunicação serial para placa BluePill (STM32F103C6T6) no Arduino. (método polling)

 // Código Exemplo para Comunicação Serial com STM32F1 (ex: Blue Pill) usando Arduino IDE

// Autor: Aguivone M.Fogia
// Data: 03/06/2025
// Descrição: Demonstrar envio e recebimento de dados via porta serial (USART1), lendo um botao e acionando led.
//    modo polling

/*
  material necessario
  1. Placa STM32F103C8T6 ou C6T6 (Blue Pill ou similar).
  2. Programador ST-Link V2 ou conversor USB-Serial (para upload e/ou comunicação).
  3. Core STM32 para Arduino IDE instalado (veja instruções no exemplo blink anterior).
  4. Configuração da Placa no Arduino IDE:
     - Ferramentas > Placa: "Generic STM32F103C series" ou similar.
     - Ferramentas > Variant: "STM32F103C6T6"
     - Ferramentas > Upload method: "STLink" ou "Serial".
     - Ferramentas > Porta: Selecione a porta COM correspondente ao seu conversor USB-Serial ou ST-Link (se aplicável para comunicação).

  --- Conexão Física (para USART1) ---
  - Conecte o pino PA9 (TX1) do STM32 ao pino RX do seu conversor USB-Serial.
  - Conecte o pino PA10 (RX1) do STM32 ao pino TX do seu conversor USB-Serial.
  - Conecte o GND do STM32 ao GND do conversor USB-Serial.
  - Alimente a placa STM32 (via ST-Link, USB ou fonte externa).

  --- Nota sobre Portas Seriais ---
  - O objeto `Serial` no core STM32 para Arduino geralmente se refere à USART1 (pinos PA9/PA10).
  - Algumas configurações de placa/core podem mapear `Serial` para a porta USB CDC (Comunicação via cabo USB direto, se a placa tiver bootloader compatível ou hardware USB nativo configurado).
  - Se você precisar usar outras portas seriais (USART2, USART3), use os objetos `Serial1`, `Serial2`, `Serial3`, etc., e conecte aos pinos correspondentes (verifique o pinout da sua placa e a documentação do core).
    - Serial1: PA9 (TX), PA10 (RX)
    - Serial2: PA2 (TX), PA3 (RX)
    - Serial3: PB10 (TX), PB11 (RX)
*/

// --- Configurações ---
  #define LED    PC13
  #define BOTAO  PA7
  long           baudRate = 9600; // Taxa de comunicação serial (bits por segundo)
  // --- Variáveis Globais ---
  String         pacote = ""; // String para armazenar dados recebidos
  unsigned long  tempo_atual;
  unsigned long  tempo_anterior;
  unsigned long  tempo_led = 100;
  int           comando_botao=0;
 
// --- Função Setup: Executada uma vez ---
void setup()
{
  // Inicializa a comunicação serial (USART1 por padrão)
  Serial.begin(baudRate);
  pinMode(LED, OUTPUT); //LED
  pinMode(BOTAO, INPUT);
  // Espera um pouco para a porta serial estabilizar (opcional, mas útil)
  delay(100);
  Serial.println("Comunicação serial com STM32F103C6T6 \n\r");
  Serial.print("Usando botão pra mudar velocidade do led ou a serial\n\r");
}

// --- Função Loop: Executada repetidamente ---
void loop()
{
  // --- Leitura de Dados da Serial ---
  if (Serial.available() > 0) { // Verifica se há dados chegando
    char caracter = Serial.read(); // Lê um caractere
       if(caracter == 'A')//vel1
       {
          Serial.println("velocidade 1 = 100 \n\r");
          tempo_led = 100;
          comando_botao = 0;
          delay(100);//debounce para botao
       }
       if(caracter == 'B')//vel2
       {
          Serial.println("velocidade 2 = 500 \n\r");
          tempo_led = 500;
          comando_botao = 1;
          delay(100);//debounce para botao
       }
       if(caracter == 'C')//vel3
       {
          Serial.println("velocidade 3 = 1000 \n\r");
          tempo_led = 1000;
          comando_botao = 2;
          delay(100);//debounce para botao
       }
     
  }
  else
  {
    if(digitalRead(BOTAO)== 1)
    {
        comando_botao++;
        if(comando_botao > 2)
        {
            comando_botao = 0;
        }
        if(comando_botao == 0)//vel1
       {
          Serial.println("velocidade 1 = 100 \n\r");
          tempo_led = 100;
       }
       if(comando_botao == 1)//vel2
       {
          Serial.println("velocidade 2 = 500 \n\r");
          tempo_led = 500;
       }
       if(comando_botao == 2)//vel3
       {
          Serial.println("velocidade 3 = 1000 \n\r");
          tempo_led = 1000;
       }
       delay(500);//debounce para botao
    }

    tempo_atual = millis();
    if(tempo_atual - tempo_anterior >= tempo_led)
        {
          digitalWrite(LED, !digitalRead(LED));
          tempo_anterior = tempo_atual;
        }
  }

}

quinta-feira, 10 de abril de 2025

Frequencímetro via usb com o ESP32 (com precisão de nanosegundos) - Arduino

     Olá pessoal depois de um bom tempo sem postar nada trago um exemplo de como usar o pino de interrupção do ESP32 para medir frequência com uma precisa até maior do que as de muitos osciloscópios comercias visto que a frequência interna no ESP está com 240MHZ(não precisei de mudar nada pois é padrão), só foi utilizada a biblioteca esp_timer.h.

       Não vou postar aqui o oscilador que foi conectado ao ESP32 pois ele foi apenas para demonstração não sendo o intuito do desse artigo. Só foi usado os pinos GPIO 14(para capturar a interrupção)  e GPIO 13( para o led), o restante já vem pronto na placa ESP32 DEV KIT.

  O código fonte é este:

#include <esp_timer.h>

#define INT1 14  
#define LED 13 
  // led de status

 
uint64_t tempo_anterior = 0;
uint64_t tempo_atual = 0;
uint64_t periodo1 = 0;
uint16_t  numero_amostras = 0;

//interrupção do pino1 
void IRAM_ATTR interrupt1() 
{  
   //tempo_atual = esp_timer_get_time();//micros(); //pega tempo em microsegundos
   tempo_atual = xthal_get_ccount();  // pegar tempo em nanosengundos
   if(tempo_atual>tempo_anterior)
   {
      periodo1 = periodo1 + (tempo_atual - tempo_anterior);
      numero_amostras++;
      if(numero_amostras > 4000)
      {
        // 13us estava dando 3160 aprox logo 243(ticks(clock)) ou seja cada clock = 0,243 ns  
    
        uint64_t periodo_buffer = periodo1/4000;
        Serial.print("periodo(ciclos)= ");
        Serial.print(periodo_buffer);
        Serial.print(" | periodo(us)= ");
        Serial.print(periodo_buffer/226);
        Serial.print(" | frequencia(hz) = ");
        Serial.println(240600000/periodo_buffer);
        periodo1 = 0;
        numero_amostras=0;
      }
   }
   /*else
   { //nem vai precisar e só descartar essa amostra 
      periodo1 = periodo1 + ((0XFFFFFFFFFFFFFFFF - tempo_anterior)+ tempo_atual); // como é de 64 bits fica grande
   }*/
   
  tempo_anterior = tempo_atual;
}

void setup() {
  Serial.begin(115200);  // Inicializa a comunicação serial que comunica com o arduino 
  pinMode(INT1, INPUT);  // Configura o primeiro pino de interrupção
  pinMode(LED, OUTPUT);  // Configura o primeiro pino de interrupção
  attachInterrupt(digitalPinToInterrupt(INT1), interrupt1, FALLING);  // Interrupção a cada  descida do sinal
  Serial.println("inicializado");
}

void loop() { 
   digitalWrite(LED, LOW);
   delay(300);
   digitalWrite(LED, HIGH);
   delay(300);
}

O vídeo de demonstração é este :

Veja a comparação com um multímetro comercial como ficou muito preciso.

formatado por : http://hilite.me/  acessando em 10/04/2025.

quinta-feira, 4 de janeiro de 2024

Frequencímetro com 2 canais usando o PIC16F887 e mostrando via serial RS232 (frequencímetro duplo)

     Olá neste primeiro projeto de 2024 vou colocar um exemplo de frequencímetro duplo para medir 2 frequências através dos CCP'S e enviar via seria, além de funções prontas para calculo da frequência e configuração do prescaler para cada faixa de frequência desejada.     

    Nos testes do simulador e no projeto físico funcionou muito bem e com muita precisão, um exemplo de simulação é visto na figura a seguir:


    Note que os leds são pra indicar que esta lendo pulsos nos pinos dos ccp's , como foi configurado para cada borda de subida logo terá a o valor de frequência menor. Para este foi colocado o mesmo sinal nos 2 canais para simular.

O código fonte :

/*
 *
 * Compilador : MPlabXC8
 * Microcontroladores compativeis : 16F886 e 887
 * Autor: Aguivone Moretti Fógia
 * Versão: 1.1.2
 * Data :  02/01/2024 - inicio de testes com oscilador interno (de 8MHZ) - 100 a 8000hz no simulador 
 * Data :  02/01/2024 - inicio de testes com oscilador interno (de 20MHZ) - 
 * descrição : Frequencimetro 
 * 
 */
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
// se igual a 8MHZ:
/* #define _XTAL_FREQ 8000000
   #pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
 */ 
// se igual a 20MHZ:
  #define _XTAL_FREQ 20000000
  #pragma config FOSC = HS        //  cristal de 20mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////////

#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///////////////////////////////////////////////////////////////////////////////////////////
//o pino R/W é conectado ao terra pois não é feita nenhuma leitura no display
//configuração:
//
// pino da porta selecionada / pino display
//
// C4 = pino de D4 do display
// C5 = pino de D5 do display
// C6 = pino de D6 do display
// C7 = pino de D7 do display
//
// C1 = pino de RS do display
// C2 = pino de EN do display
//
/*************************** configuração dos pinos **************************************************/
////////PINOS DO DISPLAY DE LCD ///////////////

//16F88
/*
 #define BIT_0              RB0
 #define BIT_1              RB1
 #define BIT_2              RB3
 #define BIT_3              RB4

 #define PIN_RS485          RA1 //pino de controle da rs485
 #define EN                 RA2
 #define RS                 RA3
///////// PINO DE SINALIZAÇÃO //////////////
#define  LED_STATUS         RA4
//////////PINOS DE ENTRADA  ///////////////
#define  BOT_FUNCAO         RB7
#define  BOT_SELECAO        RB6
#define  BOT_OK             RA6
*/
//16F887
#define  LED_STATUS         RB0
#define  LED_freq1          RB1
#define  LED_freq2          RB2

long periodo_anterior_1=0;
long periodo_anterior_2=0;
long periodo1=0;
long periodo2=0;
char  CCP1_mult,CCP2_mult;
float aux = 0;
float fator_de_div = 0;
void CCP_div_por(char CCP1_V,char CCP2_V)
{//valor da divisão do CCPs
    CCP1CON = 0X05;//captura a cada borda 
    CCP2CON = 0X05;//por padrao
    CCP1_mult = CCP1_V;
    if(CCP1_mult == 4)
    {
       CCP1CON = 0X06; 
    }
    if(CCP1_mult == 16)
    {
       CCP1CON = 0X07; 
    }
    CCP2_mult = CCP2_V;
    if(CCP2_mult == 4)
    {
       CCP2CON = 0X06; 
    }
    if(CCP2_mult == 16)
    {
       CCP2CON = 0X07; 
    } 
}
void prescaler_por(char prescaler)
{
    switch(prescaler)
    {
        case 1:///por 1
                fator_de_div =  _XTAL_FREQ/4;
                T1CON = 0X01;
        break;
        case 2: /// por 2
                fator_de_div =  _XTAL_FREQ/8;
                T1CON = 0X11;
        break;
        case 4: /// por 4
                fator_de_div =  _XTAL_FREQ/16;
                T1CON = 0X21;
        break;
        case 8:/// por 8
                fator_de_div =  _XTAL_FREQ/32;
                T1CON = 0X31;
        break;
    }
}
void interrupt interrupcoes(void)//vetor de interrupção
 {
        long periodo_atual_1  = CCPR1;//pega valores  depois vê se é pra tratar
        long periodo_atual_2  = CCPR2;        
        /*if(TMR1IF)
        { //não será usado aqui
            TMR1IF = 0;
        }*/
        if(CCP1IF)
        {
            CCP1IF = 0;//limpa flag de interrupção
                   if(periodo_atual_1> periodo_anterior_1)
                    {
                      periodo1 = periodo_atual_1 - periodo_anterior_1;
                    }
                    else
                    {
                      periodo1 = (65535 - periodo_anterior_1) + periodo_atual_1;
                    }             
            LED_freq1 = ~LED_freq1;
            periodo_anterior_1 = periodo_atual_1;    
            
        }
        if(CCP2IF)
        {
            CCP2IF = 0;//limpa flag de interrupção
                   if(periodo_atual_2> periodo_anterior_2)
                        {
                          periodo2 = periodo_atual_2 - periodo_anterior_2;
                        }
                        else
                        {
                          periodo2 = (65535 - periodo_anterior_2) + periodo_atual_2;
                        }             
            LED_freq2 = ~LED_freq2;
            periodo_anterior_2 = periodo_atual_2;
        }
        if(RCIF == 1)
        {//interrupção de recepção de dados
            char aux;
            //lembrar de por um timeout pra não ficar travado esperando dados
            RCIF = 0;//  limpa flag de interrupção de recepção
            aux = RCREG;  //deve ler para não trava o chip esperando tratar a interrupçao
            // não será utilizado aqui
        }
}
void imprime_rs232(char frase[],int tamanho)
{ //para conseguir imprimir em hexa sem problemas é melhor passar o tamanho do pacote
       int indice = 0;
       __delay_us(1);
       for(indice = 0;indice < tamanho;indice++)
       {
           while(TRMT==0);//espera enviar caracter, esvaziando o shift register , usando o trmt é mais garantido a entrega 
           TXREG = frase[indice];//no datasheet diz que ao carregar o TXREG limpa o bit TXIF           
           while(TRMT==0);
       }        
}
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)
}
void long_to_char(long valor)
{
    char Buffer_serial_out[5]; //delimitador pra facilitar jogar no excel
    Buffer_serial_out[0]='0';
    Buffer_serial_out[1]='0';
    Buffer_serial_out[2]='0';
    Buffer_serial_out[3]='0';
    Buffer_serial_out[4]='0';
    if(valor < 65535)
    {
        while(valor>=10000)
             {
                 valor = valor - 10000;
                 Buffer_serial_out[0]++;
             }
        while(valor>=1000)
           {
               valor = valor - 1000;
               Buffer_serial_out[1]++;
           }
        while(valor>=100)
           {
               valor = valor - 100;
               Buffer_serial_out[2]++;
           }
        while(valor>=10)
           {
               valor = valor - 10;
               Buffer_serial_out[3]++;
           }
        while(valor>=1)
           {
               valor = valor - 1;
               Buffer_serial_out[4]++;
           }
        imprime_rs232(Buffer_serial_out ,5);
    }

}

//*******************************Rotina principal*********************************************/

void main(void)
{    
    TRISA = 0XF7;//configura portA  pino habilita rs    
    TRISB = 0X00;//configura portB
    TRISC = 0X06;//configura portC //tx é saida
    TRISD = 0X00;//configura portD  // somente no 16F887
    TRISE = 0XFF;//configura portD     
    PORTA = 0;
    PORTB = 0;//deixa pino tx em nivel alto
    PORTC = 0;
    PORTD = 0;// somente no 16F887 
    GIE = 0;//já desliga as interrrupções para terminar as configurações
    OPTION_REG = 0X80; // pull up desabilitados e prescaler para timer 0 - desabilita watchdog
    INTCON = 0;//desabilita todas as interrupções 
    //CMCON = 0X07;//desliga comparadores  no 16F887 é outro
    //---------------------------------------------------------
   // CCP1CON = 0X05;   //Capture mode, every rising edge
   // CCP2CON = 0X05;
     CCP_div_por(1,1);// modo do ccp dividido por 1,4 ou 16.
    //---------------------------------------------------------
    ANSEL = 0X00; //portas digitais e AN0 e AN1.
    ANSELH = 0X00; //portas digitais e AN8 e AN13.// 
    OSCTUNE = 0X1F;  // oscilador vel maxima(para que o baud rate tambem dê certo)
    // ----------------------------------------------------------------------------------------------------------
    // para oscilador interno de 8 mhz use :           
         // OSCCON = 0XFE ; // oscilador interno com frequencia de 8mhz ->16F88
       //  OSCCON = 0XF5 ; // oscilador interno com frequencia de 8mhz ->16F887         
     //----------------------------------------------------------------------------------------------------------    
    // para cristal de 20 Mhz use:   
     OSCCON = 0XFE; // oscilador de 20mhz
     //----------------------------------------------------------------------------------------------------------
    inicializa_RS232(19200,1,0);
    CCP1IE = 1;//habilita interrupçao de ccp
    CCP2IE = 1;//habilita interrupçao de ccp
   // T1CON=1;	//Timer on(para fazer as contagem)) qunado uas a afunção abaixo não precisa por este
    prescaler_por(1);
   // TMR1IE = 1;//habilita interrupção de estouro de timer para verificar quantos estouros houve não será usado
    PEIE = 1;//habilita interrupção de hardware
    GIE = 1;
    imprime_rs232("Frequencimetro duplo com pic \n\r",30);
   for(;;)
     {
       __delay_ms(10);
       GIE = 0;
       //imprime o valor da frequencia atual a cada 200ms 
       imprime_rs232("\n\r FREQUENCIAS : ",17);
       aux =  fator_de_div / periodo1;
       aux  = aux*CCP1_mult;
       if(aux > 99999)
       {
           aux/1000;
           periodo1  = (long)aux;
           long_to_char(periodo1);//valor de frequencia calculada
           imprime_rs232("KHz ",3);
       }
       else
       {
           periodo1  = (long)aux;
           long_to_char(periodo1);//valor de frequencia calculada
           imprime_rs232("Hz - freq.2 = ",3);
       }
       aux =  fator_de_div / periodo2;
       aux  = aux*CCP2_mult;
       if(aux > 99999)
       {
           aux/1000;
           periodo2  = (long)aux;
           long_to_char(periodo2);//valor de frequencia calculada
           imprime_rs232("KHz ",3);
       }
       else
       {
           periodo2  = (long)aux;
           long_to_char(periodo2);//valor de frequencia calculada
           imprime_rs232("Hz ",3);
       }
       LED_STATUS = ~LED_STATUS; 
       GIE = 1;
     }//loop infinito
}


//  Notas:  *****************
/*
 estando com o CCP1 e CCP2 sem divisão ( div por 1) e com cristal de 20mhz
 * a faixa de range confiavel é aprox :
 * prescaler para  1 :  a partir de 80hz a 100Khz(estimado)
 * prescaler para  2 :  a partir de 40hz a 70khz(etimado) 
 * prescaler para  4 :  a partir de 10hz a 30khz(simulado)
 * prescaler para  8 :  a partir de 10hz a 25khz(simulado)
 * 
 * para faixa maxima o simulador proteus é limitado o ideal é usar um frequencimetro 
 * isso se deve ao estouro do timer para frequencia baixa(se for operar nessas frequencia use clock menor)
 */

formatado por : http://hilite.me/  acessando em 04/01/2024.