Baixe o app para aproveitar ainda mais
Prévia do material em texto
UTFPR LEONARDO BRAGA DE CRISTO WILLIAN ALVES SANTIAGO Sistema on/off para controle e medição de temperatura Documento para composição de nota na disciplina Sistemas Operacionais do curso de Engenharia Eletrônica da Universidade Tecnológica Federal do Paraná sob orientação do professor Luiz Fernando Copetti. Curitiba, 18 de dezembro de 2023 Introdução Este projeto tem por objetivo demonstrar o uso de um sistema operacional embarcado e as suas funcionalidades. O sistema operacional escolhido é o freeRTOS. O projeto escolhido é um sistema de controle “on-off” para controle de temperatura. Componentes Os componentes utilizados são apresentados na lista abaixo: ● Sensor de temperatura - LM35 ● Capacitor de poliester - 1uF ● Resistor - 100 Ohm ● Módulo relé ● Microcontrolador - TM4C1294NCPDT Figura 1 - Esquemático do Projeto Objetivo do Projeto O projeto tem por objetivo o controle da temperatura limite escolhida. Para isso um atuador, que seria uma lâmpada incandescente, é utilizado para aquecer quando a temperatura ambiente estiver abaixo do esperado e desligado quando estiver acima do esperado. Protocolo UDP (User Datagram Protocol) é um protocolo de comunicação da camada de transporte usado em redes de computadores. Faz parte da família de protocolos da Internet (TCP/IP) e é uma alternativa ao protocolo de controle de transmissão (TCP). Alguns recursos básicos do UDP incluem: Sem conexão: UDP é um protocolo sem conexão. Ou seja, não há conexão até que os dados sejam transmitidos. Cada pacote (datagrama) é processado separadamente. Não confiável: Ao contrário do TCP, o UDP não oferece confiabilidade na transmissão de dados. Não há garantia de que os dados serão entregues ao destino, e não há mecanismos de retransmissão em caso de perda de pacotes. Baixa sobrecarga: A falta de mecanismos de confiabilidade implica em uma sobrecarga menor no processo de comunicação. Isso torna o UDP mais eficiente em termos de latência, sendo muitas vezes preferido em aplicações que demandam respostas rápidas, como jogos online e transmissões de áudio/vídeo em tempo real. Transmissão de broadcast e multicast: O UDP suporta transmissões de dados para múltiplos destinos simultaneamente através de broadcast e multicast. Isso é útil em aplicações como streaming de vídeo para vários receptores. Cabeçalho simples: O cabeçalho do UDP é mais simples que o do TCP, contendo apenas informações essenciais, como portas de origem e destino, e o comprimento do datagrama. Isso reduz a sobrecarga de dados e acelera o processo de encapsulamento e desencapsulamento. Comunicação O projeto possui uma interface desenvolvida em linguagem python de programação. Esta interface possui duas threads, uma para recebimento de pacotes UDP e outra para envio de pacotes UDP. A comunicação é feita via sockets em uma rede local, por meio do roteador. Na figura 2, a interface é apresentada. Ela possui 2 campos chamados IP e Porta que devem ser preenchidos com o IP e Porta do microcontrolador. No projeto em questão, foi utilizado o IP 192.168.18.61 com mascará de sub-rede 255.255.255.0 e porta 12345. No campo da esquerda estão os pacotes enviados e na direita os pacotes recebidos. Existe um campo para envio de mensagens, nesse campo devemos enviar uma variável do tipo float pressionando o botão Enviar. Esta variável irá ser utilizada como temperatura limite, para comparação no sistema de controle de temperatura. Caso não for enviada a temperatura limite será de 25 C. Há também um botão para limpar as mensagens da interface e um botão para sair. Python Figura 2 - Interface desenvolvida para comunicação com o sistema embarcado. Desenvolvimento da interface O código utilizado importa a biblioteca PySimpleGUI para criação das interfaces gráficas, a biblioteca socket para troca de mensagens UDP, e a biblioteca threading para termos a capacidade de multiprocessamento, já que teremos que tanto receber pacotes como enviá-los. import PySimpleGUI as sg import socket import threading Figura 3 - Bibliotecas importadas no python. Python A função para recebimento de mensagens é invocada como um thread, a implementação dela é apresentada na figura 3. Ela recebe 2 campos, o client_socket, que é criado com a biblioteca socket para transferência de arquivos via UDP, e o window para manipularmos as mensagens recebidas na interface gráfica. A função recv tenta receber até 1024 bytes de dados do socket. O resultado é armazenado na variável data. Os dados são inseridos na janela de texto direita da figura 2. Se um erro ocorrer apenas saímos. O bloco está dentro de um loop infinito, pois a função, na verdade, é invocada como uma thread. def receive_messages(client_socket, window): while True: try: data = client_socket.recv(1024) if not data: break window['-OUTPUT-PC'].print(data.decode('utf-8')) except ConnectionResetError: break Figura 4 - Thread de recebimento de pacotes. Na função main, a função principal, escolhemos o tema DarkGrey1 para a interface, criamos os campos de texto por meio de listas, itens na mesma lista são apresentados lado a lado na tela, as listas dentro de layout serão apresentadas uma abaixo da outra. O nome da interface chama-se HOST. Python Dentro de um loop infinito analisamos os eventos da interface. Os possíveis eventos são: ● Sair: - Fecha a interface. ● Conectar: - Abre um socket UDP com o HOST na mesma rede. ● Enviar: - Envia pacotes UDP para o HOST remoto. ● Limpar: - Remove as mensagens da interface. Existem outros eventos que são intrínsecos da biblioteca pysimplegui, tais como o fechamento de janela e minimizar. def main(): sg.theme('DarkGrey1') layout = [ [sg.Text('IP:'), sg.InputText(key='-IP-')], [sg.Text('Porta:'), sg.InputText(key='-PORTA-'),sg.Text('',key = '-IP-TIVA')], [sg.Button('Conectar', key='-CONECTAR-')], [sg.Column([[sg.Text('ENVIADOS'),sg.Text('RECEBIDOS',justifica tion='right')]], justification='center')], [sg.Multiline(size=(45, 10), key='-OUTPUT-TIVA', disabled=True),sg.Multiline(size=(45, 10), key='-OUTPUT-PC', disabled=True)], [sg.Text('Mensagem:'), sg.InputText(key='-MENSAGEM-')], [sg.Button('Enviar', key='-ENVIAR-'),sg.Button('Limpar', key='-LIMPAR-'),sg.Button('Sair')] ] window = sg.Window('HOST', layout) client_socket = None while True: event, values = window.read() if event == sg.WINDOW_CLOSED or event == 'Sair': break elif event == '-CONECTAR-': ip = values['-IP-'] porta = int(values['-PORTA-']) try: client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.connect((ip, porta)) # Iniciar uma thread para receber mensagens threading.Thread(target=receive_messages, args=(client_socket, window), daemon=True).start() window['-IP-TIVA'].update(f"Conectado ao servidor em {ip}:{porta}") except Exception as e: sg.popup_error(f"Erro ao conectar: {str(e)}") elif event == '-ENVIAR-' and client_socket: mensagem = values['-MENSAGEM-'] if mensagem: client_socket.sendall(mensagem.encode('utf-8')) window['-OUTPUT-TIVA'].print(mensagem) elif event == '-LIMPAR-': window["-OUTPUT-TIVA"].update("") if client_socket: client_socket.close() window.close() Figura 5 - Função Main. Programa do microcontrolador O microcontrolador utilizado é o TM4C1294NCPDT, que é um ARM cortex m4 de 32 bits. Ele possui controlador de rede, que utiliza DMA para receber e enviar pacotes. Os pacotes são colocados na RAM, em um endereço descrito por uma estrutura chamada descritor de arquivos. Quando um pacote é recebido ou enviado, uma interrupção é gerada, esta interrupção é tratada por uma função que envia o resultado para a biblioteca LWIP. A LWIP possui já implementado a interpretação dos pacotes para socket, http, ftp, ssh, icmp e vários outros protocolos que computadores de uso geral utilizam. É possível programar com a LWIP de duas formas, a primeira é de forma convencional, por interrupções e super loop, a segunda, o nosso caso, é por meio de umrtos. Utilizamos o freeRTOS, que vai monitorar está chegada e envio de pacotes. Cada microcontrolador precisa fazer o port, que é a configuração específica do microcontrolador, para o uso da LWIP. No nosso caso, a texas fez um port muito antigo que hoje é obsoleto, então foi escolhido o port utilizado pela stm. As tasks utilizadas estão presentes na figura 5. São descritas da seguinte forma: ● udpProcessingTask:Recebe dados de uma fila, em que o sensor de temperatura envia, e decide se o atuador irá ligar ou não. ● sensorTask: Lê dados de temperatura do sensor LM35, cálcula uma média de 10 medidas espaçadas de 500ms e os envia para uma fila que será lida pela task udpProcessingTask. Também envia dados via rede ethernet para o HOST remoto, os dados são a temperatura atual. ● udpReceiver:Recebe pacotes ethernet do HOST remoto e redefine a temperatura limite de controle. ● UpLwIp: Grava em flash o MAC do microcontrolador e obtém um IP local. Cria as tasks udpReceiver e sensorTask. Esta task pode ser deletada após isso. ● IDLE TASK: É a task padrão que inicializa o clock, as configurações iniciais do conversor ad utilizado para ler a tensão do sensor de temperatura, da uart, da criação das filas e dos semáforos. Além da criação das tasks UpLwIp e udpProcessingTask. Para a comunicação entre tasks utilizamos filas, para gerenciar condições de corrida utilizamos semáforos binários. Para a serial e para o envio e recebimento de pacotes foram utilizados semáforos. No sensor não foi necessário pois não foi um recurso compartilhado. Figura 6 - Tasks Inicialmente o programa grava o MAC e pega um IP na rede local, após isso a task do sensor lê 10 temperaturas, tira uma média e envia para uma fila, que é lida por uma task de processamento, esta task decide se o atuador ira ligar.A task do sensor também envia as temperaturas colocadas na fila para a interface python no computador via socket. O computador pode enviar , via socket, pacotes UDP para recebimento no microcontrolador, estes pacotes são interpretados como temperaturas, é a nossa variável de controle de temperatura. Conclusões A implementação do freeRTOS juntamente com a pilha TCP/IP da LWIP é uma solução inteligente em relação a programação por interrupções e máquina de estados. Apenas deve-se ter o cuidado com as condições de corrida, o uso de periféricos compartilhados, regiões críticas, etc. Inicialmente utilizei a serial para debug sem semáforo binário e com o escalonamento algumas tarefas utilizavam a serial sem a tarefa corrente concluir sua mensagem, com os semáforos esse problema foi solucionado. A fila resolve de forma inteligente a comunicação entre as tarefas. Dentro da LWIP existe um tipo de fila chamada mail box, que pega os pacotes recebidos, que de forma convencional seriam tratados via interrupção. Referências ● https://www.nongnu.org/lwip/2_0_x/group__socket.html ● https://www.freertos.org/ ● https://www.ti.com/lit/ds/symlink/lm35.pdf ● https://www.youtube.com/@gustavodenardin https://www.nongnu.org/lwip/2_0_x/group__socket.html https://www.freertos.org/ https://www.ti.com/lit/ds/symlink/lm35.pdf https://www.youtube.com/@gustavodenardin C/C++ Código do Projeto (Microcontrolador) #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "driverlib/gpio.h" #include "driverlib/pin_map.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/uart.h" //DRIVER UART #include "utils/uartstdio.h" //DRIVER UART #include "FreeRTOS.h" //RTOS UTILIZADO #include "task.h" //Manipulação de TASKS #include "lwiplib.h" // LWIP #include "pinout.h" // pinout para config ethernet #include "lwip/sockets.h" //bilioteca socket da LWIP #include "driverlib/adc.h" //biblioteca adc para leitura do sensor //Temperatura de controle float temperatura_limite = 25; int sockfd; struct sockaddr_in client_addr; //Protótipo das funcoes e tasks void DisplayIPAddress(uint32_t ui32Addr); void lwIPHostTimerHandler(void); void ConfigureUART(void); void UpLwIP(void *param); void initialize(void); void udpReceiver(void *pvParameters); void adcConfig(void); float readTemp(void); void sensorTask(void *pvParameters) ; void ConfigureAtuador(void); float media(float vetor[]); //Pino do atuador #define GPIO_PIN_ATUADOR GPIO_PIN_2 //IP interno do PC #define DEST_IP_ADDR "192.168.18.53" //Porta interna do PC #define DEST_PORT 54321 //Porta utilizada para receber os pacotes ethernet #define PORT 12345 //Estruttura utilizada pela LWIP struct netif sNetIF; //Clock do Sistema, utilizado por oitras funcoes uint32_t g_ui32SysClock; //Utilizado para controlar se o IP foi obtido ou nao volatile BaseType_t lwip_link_up = pdFALSE; //Variavel que armazena o IP obtido uint32_t g_ui32IPAddress; //Fila com dados Recebidos via socket QueueHandle_t udpQueue; //Semaforo UDP para sincronizar o uso do hardware de envio e recebimento de pacotes SemaphoreHandle_t xUDPDataSemaphore; SemaphoreHandle_t serialSemaphore; // Mostra o IP obtido na serial void DisplayIPAddress(uint32_t ui32Addr) { static volatile char pcBuf[16]; // // Convert the IP Address into a string. // sprintf(pcBuf, "%d.%d.%d.%d", (int)(ui32Addr & 0xff), (int)((ui32Addr >> 8) & 0xff), (int)((ui32Addr >> 16) & 0xff), (int)((ui32Addr >> 24) & 0xff)); // // Display the string. // UARTprintf("%s\n",pcBuf); } //************************************************************ ***************** // // Necessaria para a LWIP // //************************************************************ ***************** //Funcao auxiliar para ler o IP obtido void lwIPHostTimerHandler(void) { uint32_t ui32Idx, ui32NewIPAddress; // // Get the current IP address. // ui32NewIPAddress = lwIPLocalIPAddrGet(); // // See if the IP address has changed. // if(ui32NewIPAddress != g_ui32IPAddress) { // // See if there is an IP address assigned. // if(ui32NewIPAddress == 0xffffffff) { // // Indicate that there is no link. // //UARTPutString(UART0_BASE, "Waiting for link.\n\r"); } else if(ui32NewIPAddress == 0) { // // There is no IP address, so indicate that the DHCP process is // running. // //UARTPutString(UART0_BASE, "Waiting for IP address.\n\r"); } else { // // Display the new IP address. // lwip_link_up = pdTRUE; //UARTPutString(UART0_BASE, "IP Address: "); DisplayIPAddress(ui32NewIPAddress); //UARTPutString(UART0_BASE, "\n\rOpen a browser and enter the IP address.\n\r"); } // // Save the new IP address. // g_ui32IPAddress = ui32NewIPAddress; // // Turn GPIO off. // // MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, ~GPIO_PIN_1); } // // If there is not an IP address. // if((ui32NewIPAddress == 0) || (ui32NewIPAddress == 0xffffffff)) { // // Loop through the LED animation. // for(ui32Idx = 1; ui32Idx < 17; ui32Idx++) { // // Toggle the GPIO // #if 0 MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, (MAP_GPIOPinRead(GPIO_PORTN_BASE, GPIO_PIN_1) ^ GPIO_PIN_1)); // DelayTask(1000/ui32Idx); #endif } } } float readTemp(void){ ADCIntClear(ADC0_BASE, 3); ADCProcessorTrigger(ADC0_BASE, 3); // Aguardar a conclusão da conversão while (!ADCIntStatus(ADC0_BASE, 3, false)) { } // Ler o valor convertido float temperaturaCelsius; uint32_t adcResult; ADCSequenceDataGet(ADC0_BASE, 3, &adcResult); // Converte a leitura em temperatura em graus Celsius // Para 5V e ADC de 12 bits, a fórmula é (leitura * Vcc / 4095) / 0.01 temperaturaCelsius = (adcResult * 5 / 4095.0) / 0.01; if(temperaturaCelsius>=20) temperaturaCelsius-=20;//OFFSET DO SENSOR // Agora 'voltage' contém a tensão lida do pino analógico return temperaturaCelsius; } //Funcao de configuracao do ADC void adcConfig(void){ SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3); // Configurara sequência de amostragem do ADC ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END); ADCSequenceEnable(ADC0_BASE, 3); ADCIntClear(ADC0_BASE, 3); } //Funcao de configuracao da UART void ConfigureUART(void) { // // Enable the GPIO Peripheral used by the UART. // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // // Enable UART0 // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); // // Configure GPIO Pins for UART mode. // ROM_GPIOPinConfigure(GPIO_PA0_U0RX); ROM_GPIOPinConfigure(GPIO_PA1_U0TX); ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1); // // Initialize the UART for console I/O. // UARTStdioConfig(0, 115200, g_ui32SysClock); } //Funcao de configuracao do pino do atuador void ConfigureAtuador(void){ // Ativar o port E pino 5 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); // Esperar até que o porto E esteja pronto while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)); // Configurar o pino como saída GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_ATUADOR); //Deixa desligado por padrao GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_ATUADOR, 0); } //Task de inicializacao de MAC e IP, Cria a task do socket também, pois ela depende do IP e do MAC iniciados void UpLwIP(void *param) { UARTprintf("UpLwIP - T1\n"); uint32_t ui32User0, ui32User1; //uint32_t ui32Loop; uint8_t pui8MACArray[6]; (void)param; // // Configure the device pins. // PinoutSet(true, false); vTaskDelay(1500); //UARTPutString(UART0_BASE, "Ethernet lwIP example\n\r"); // Configure the hardware MAC address for Ethernet Controller filtering of // incoming packets. The MAC address will be stored in the non-volatile // USER0 and USER1 registers. ui32User0 = 0x001AB6; ui32User1 = 0x032DCE; MAP_FlashUserSet(ui32User0, ui32User1); MAP_FlashUserGet(&ui32User0, &ui32User1); if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff)){ //Caso nao tenha MAC ficamos aqui while(1) { } } pui8MACArray[0] = ((uint8_t)(ui32User0 >> 0) & 0xff); pui8MACArray[1] = ((uint8_t)(ui32User0 >> 8) & 0xff); pui8MACArray[2] = ((uint8_t)(ui32User0 >> 16) & 0xff); pui8MACArray[3] = ((uint8_t)(ui32User1 >> 0) & 0xff); pui8MACArray[4] = ((uint8_t)(ui32User1 >> 8) & 0xff); pui8MACArray[5] = ((uint8_t)(ui32User1 >> 16) & 0xff); // Iniciamos a LWIP como DHCP, vamos obter o IP automaticamente lwIPInit(configCPU_CLOCK_HZ, pui8MACArray, 0, 0, 0, IPADDR_USE_DHCP, &sNetIF); while(lwip_link_up != pdTRUE){ vTaskDelay(500); } //Cria a task que recebe tasks xTaskCreate(udpReceiver, "UDPServer", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 6, NULL); //Cria a task de leitura do sensor, que envia dados para o cliente do sensor xTaskCreate(sensorTask, "Sensor 1", 1024, NULL, tskIDLE_PRIORITY + 5, NULL); //vTaskDelete(NULL); while(1) { vTaskDelay(10000); } } // Configurar o socket como não bloqueante com um timeout void configureNonBlockingSocket(int sockfd, int timeout_ms) { lwip_fcntl(sockfd, F_SETFL, O_NONBLOCK); // Configura o socket como não bloqueante lwip_setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout_ms, sizeof(timeout_ms)); // Configura o timeout de recepção lwip_setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout_ms, sizeof(timeout_ms)); // Configura o timeout de envio } //Task de recebimento de pacotes via socket void udpReceiver(void *pvParameters) { struct sockaddr_in server_addr;//, client_addr; /*int sockfd,*/ int new_sockfd; socklen_t sin_len = sizeof(client_addr); char buffer[20]; sockfd = lwip_socket(AF_INET, SOCK_DGRAM, 0);// Cria um socket UDP memset(&server_addr, 0, sizeof(server_addr));// Zera o a estrutura server_addr //Seta o server_addr server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY;// Pega um IP automatico server_addr.sin_port = htons(PORT); //Associa o IP automaticamente lwip_bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); configureNonBlockingSocket(sockfd,100); while (1) { xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("udpReceiver - T2 - LOOP\n"); xSemaphoreGive(serialSemaphore); //Escuta no socket sockfd e coloca no buffer xSemaphoreTake(xUDPDataSemaphore, pdMS_TO_TICKS(1000));// Pega o semaforo UDP new_sockfd = lwip_recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &sin_len); // Se dados forem recebidos if(new_sockfd > 0) { // Imprimir informações do cliente char client_ip[16]; inet_ntoa_r(client_addr.sin_addr, client_ip, sizeof(client_ip)); xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("Cliente IP: %s, Porta: %d\n", client_ip, ntohs(client_addr.sin_port)); UARTprintf("udpReceiver - T2 - LOOP- > 0\n"); //buffer[new_sockfd] = '\0'; // Fim da string recebida temperatura_limite = atof(buffer); UARTprintf("%s\n",buffer); xSemaphoreGive(serialSemaphore); } xSemaphoreGive(xUDPDataSemaphore); // Libera o Semaforo UDP vTaskDelay(1000/portTICK_PERIOD_MS); } } //Task do atuador void udpProcessingTask(void *pvParameters) { char receivedData[20]; float Temp; //Para DEBUG xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("Task ATUADOR - T3\n"); xSemaphoreGive(serialSemaphore); while (1) { if (xQueueReceive(udpQueue, receivedData, 0)) {//Espera dado na fila UDP // Converte a string para float xSemaphoreTake(serialSemaphore, portMAX_DELAY); Temp = atof(receivedData); char buf[10]; sprintf(buf, "%f",Temp);//Converte para String para envio via UDP UARTprintf("Temp %s \n",buf ); sprintf(buf, "%f",temperatura_limite);//Converte para String para envio via UDP UARTprintf("Limite %s \n",buf ); xSemaphoreGive(serialSemaphore); if(Temp<=temperatura_limite){ // Acionar atuador = Liga a Lampada xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("ATUADOR ON \n",buf ); xSemaphoreGive(serialSemaphore); GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_ATUADOR, GPIO_PIN_ATUADOR); }else{ // Desativa o atuador = Desliga a Lampada xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("ATUADOR OFF \n",buf ); xSemaphoreGive(serialSemaphore); GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_ATUADOR, 0); } } vTaskDelay(1000/portTICK_PERIOD_MS); } } //Task do sensor 1 void sensorTask(void *pvParameters) { xSemaphoreTake(serialSemaphore, portMAX_DELAY); UARTprintf("TASK SENSOR \n" ); xSemaphoreGive(serialSemaphore); static int count = 0; float leituras[10]; float temperaturaCelsius; char leituraBuffer[20]; while (1) { temperaturaCelsius = readTemp(); if(count==10){ count = 0 ; temperaturaCelsius = media(leituras); sprintf(leituraBuffer, "%f",temperaturaCelsius );//Converte para String para envio via UDP xQueueSend(udpQueue, leituraBuffer, 0); //Coloca o dado recebido na fila xSemaphoreTake(xUDPDataSemaphore, portMAX_DELAY);//Pega o semaforo para o UDP // Envia o dado if(sockfd!=-1){ lwip_sendto(sockfd, leituraBuffer, strlen(leituraBuffer), 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); } xSemaphoreGive(xUDPDataSemaphore);// Libera o Semaforo UDP }else{ leituras[count] = temperaturaCelsius; count++; } vTaskDelay(500/portTICK_PERIOD_MS);// Bloqueia para dar espaco para as outras tasks trabalharem } } float media(float vetor[]){ int i; float media = 0; for(i=0;i<10;i++) media+=vetor[i]; return media/10; } //Task IDLE int main(void) { MAP_SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ); // PLL 120 MHz g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |SYSCTL_OSC_MAIN |SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000); //Habilita stack extra e interrupcoes floating-points MAP_FPUEnable(); MAP_FPULazyStackingEnable(); //Configura a UART para Debug ConfigureUART(); //Configura o pino do atuador (E2) ConfigureAtuador(); //Configura o adc para ler os sensores adcConfig(); //Cria um semaforo para o modulo ethernet xUDPDataSemaphore = xSemaphoreCreateMutex(); //Cria um semaforo para a serial serialSemaphore = xSemaphoreCreateMutex();//Cria uma fila de 5 strings de 20 caracteres para o sensor udpQueue = xQueueCreate(5, sizeof(char[20])); //Cria a task do atuador xTaskCreate(udpProcessingTask, "UDPProcessing", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 5, NULL); //Cria a task que seta o MAC da placa e pega um IP da rede local automaticamente //Dentro há outra task que cria um socket de escuta xTaskCreate(UpLwIP, "LwIP Task", 1024, NULL, tskIDLE_PRIORITY + 5, NULL); //Inicia o escalonador vTaskStartScheduler(); return 0; } Figura 7 - Código completo do microcontrolador comentado.
Compartilhar