Skocz do zawartości

[C] [STM32] Czujnik alkoholu MQ-3 -alkomat - protokół komunikacyjny


Erdi5073

Pomocna odpowiedź

Witam, czy potrafi ktoś mi pomóc w napisaniu prostego protokołu komunikacyjnego opierającego się na buforze kołowym?

Protokół ma posiadać funkcje wyboru od jakiej wartości pomiaru dioda na pinie 5 ma się zapalić, oraz funkcje startującą pomiar, który następnie ma być wyświetlony w terminalu wyświetlony w terminalu

   #include "stm32f10x.h"
   #include "stm32f1xx_nucleo.h"
   #include <stdio.h>

   char bufor_rx_USART2[256];
   char bufor_tx_USART2[256];
   char *p_bufor_rx_USART2;
   int bufor_rx_USART2_rozmiar;
   char *p_rx_USART2_in;
   char *p_rx_USART2_out;
   char *p_bufor_tx_USART2;
   int bufor_tx_USART2_rozmiar;
   char *p_tx_USART2_in;
   char *p_tx_USART2_out;

   void USART2_IRQHandler(void) {

           if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
                   *p_rx_USART2_in = USART_ReceiveData(USART2);
                   p_rx_USART2_in++;
                   if (p_rx_USART2_in >= (p_bufor_rx_USART2 + bufor_rx_USART2_rozmiar)) {
                           p_rx_USART2_in = p_bufor_rx_USART2;
                   }
           }

           if (USART_GetITStatus(USART2, USART_IT_TXE) != RESET) {
                   if (p_tx_USART2_in != p_tx_USART2_out) {
                           USART_SendData(USART2, *p_tx_USART2_out);
                           p_tx_USART2_out++;
                           if (p_tx_USART2_out >= (p_bufor_tx_USART2 + bufor_tx_USART2_rozmiar)) {
                                   p_tx_USART2_out = p_bufor_tx_USART2;
                           }
                   } else {

                           USART_ITConfig(USART2, USART_IT_TXE, DISABLE); // disable tx interrupt
                   }
           }
   }

   int main(void) {

           USART_InitTypeDef USART_InitStructure;
           NVIC_InitTypeDef NVIC_InitStructure;
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
           RCC_ADCCLKConfig(RCC_PCLK2_Div6);

           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
           RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
           RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);




           GPIO_InitTypeDef usart_gpio;
           GPIO_StructInit(&usart_gpio);
            GPIO_StructInit(&usart_gpio);
            usart_gpio.GPIO_Pin = GPIO_Pin_2;
            usart_gpio.GPIO_Mode = GPIO_Mode_AF_PP;
            GPIO_Init(GPIOA, &usart_gpio);

            usart_gpio.GPIO_Pin = GPIO_Pin_3;
            usart_gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
            GPIO_Init(GPIOA, &usart_gpio);

           //Pin dla diody:
           GPIO_InitTypeDef dioda;
           GPIO_StructInit(&dioda);
           dioda.GPIO_Pin = GPIO_Pin_5;
           dioda.GPIO_Mode = GPIO_Mode_Out_PP;
           GPIO_Init(GPIOA, &dioda);

           GPIO_InitTypeDef adc_gpio;
            GPIO_StructInit(&adc_gpio);
            adc_gpio.GPIO_Pin = GPIO_Pin_0;
            adc_gpio.GPIO_Mode = GPIO_Mode_AIN;
            GPIO_Init(GPIOA, &adc_gpio);



           //Pin dla przycisku
           GPIO_InitTypeDef przycisk;
           GPIO_StructInit(&przycisk);
           przycisk.GPIO_Pin = GPIO_Pin_13;
           przycisk.GPIO_Speed = GPIO_Speed_50MHz;
           przycisk.GPIO_Mode = GPIO_Mode_IPU;
           GPIO_Init(GPIOC, &przycisk);

           //Konfiguracja USARTA:
           USART_InitStructure.USART_BaudRate = 9600;
           USART_InitStructure.USART_WordLength = USART_WordLength_8b;
           USART_InitStructure.USART_StopBits = USART_StopBits_1;
           USART_InitStructure.USART_Parity = USART_Parity_No;
           USART_InitStructure.USART_HardwareFlowControl =
                           USART_HardwareFlowControl_None;
           USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
           USART_Init(USART2, &USART_InitStructure);
           USART_Cmd(USART2, ENABLE);

           NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
           NVIC_Init(&NVIC_InitStructure);
           USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
           USART_ITConfig(USART2, USART_IT_TXE, ENABLE);

            ADC_InitTypeDef adc;
            ADC_StructInit(&adc);
            adc.ADC_ContinuousConvMode = ENABLE;
            adc.ADC_NbrOfChannel = 1;
            adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
            ADC_Init(ADC1, &adc);

            ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5);
            ADC_Cmd(ADC1, ENABLE);

            ADC_ResetCalibration(ADC1);
            while (ADC_GetResetCalibrationStatus(ADC1));

            ADC_StartCalibration(ADC1);
            while (ADC_GetCalibrationStatus(ADC1));

            ADC_TempSensorVrefintCmd(ENABLE);
            ADC_SoftwareStartConvCmd(ADC1, ENABLE);

           p_bufor_rx_USART2 = &bufor_rx_USART2[0];
                   bufor_rx_USART2_rozmiar = sizeof(bufor_rx_USART2);
                   p_rx_USART2_in = &bufor_rx_USART2[0];
                   p_rx_USART2_out = &bufor_rx_USART2[0];
                   p_bufor_tx_USART2 = &bufor_tx_USART2[0];
                   bufor_tx_USART2_rozmiar = sizeof(bufor_tx_USART2);
                   p_tx_USART2_in = &bufor_tx_USART2[0];
                   p_tx_USART2_out = &bufor_tx_USART2[0];

           while (1) {

           }
   }

Link do komentarza
Share on other sites

Jeżeli na drodze informacji musisz (lub uważasz że powinieneś - dlaczego?) wstawić buforowanie to zrób to, ale protokół jaki sobie przyjmiesz nie ma z tym nic wspólnego. Bufor to taki zbiornik do którego z jednej strony znaki są wpychane przez ich "producenta" a z drugiej są wyciągane przez ich "konsumenta". Obie rzeczy dzieją się asynchornicznie. Jeżeli wstawisz bufor np. w strumień danych obieranych przez UART to producentem danych będzie odbiornik UART a konsumentem jakiś proces (funkcja) która te znaki pobiera i analizuje. Buforowanie uniezależnia jedno od drugiego: znaki przychodzą w przypadkowych momentach a ich analizator może chwilami "nie mieć czasu" bo np. procesor robi akurat coś ważniejszego. Wtedy bufor "rośnie". Z kolei gdy analizator ma dużo mocy obliczeniowej to wysysa wszystko z bufora i wtedy bufor jest opróżniony: jeden znak wpada i od razu jest wyciągany przez drugą stronę. Bufor kołowy jest szczególną implementacją bufora danych, ale on "nie rozumie" tego co przechowuje więc nie ma związku z protokołem. Zadaj bardziej konkretne pytanie, bo sam bufor kołowy jest chyba dość prostym pomysłem: masz obszar pamięci i dwa wskaźniki: zapisu i odczytu. Jeden uaktualniasz gdy znak do bufora wchodzi, drugi gdy ktoś odczytuje. Oba wskaźniki się "gonią" modulo długość obszaru pamięci, ale nigdy odczyt nie może przegonić zapisu i odwrotnie. To właściwie jedyne dwa ograniczenia: nie wolno pisać gdy bufor jest pełny i nie wolno czytać gdy jest pusty. Dla ułatwienia możesz wprowadzić trzecią zmienną: licznik znaków.

Link do komentarza
Share on other sites

Dziękuje za objaśnienie, ale to nie rozwiązało mojego głównego problemu, mianowicie zagadnienia związanego z protokołem komunikacyjnym. W jaki sposób można stworzyć taki protokół, aby przy pomocy komend wybierać daną operacje która ma zostać wykonana, np, aby wyglądało to tak:

urządzenie>Help

Start

change level

...

urządzenie>Start

Adc = ....

urządzenie>

Link do komentarza
Share on other sites

Trudno, nie zgadłem jak wygląda Twój główny problem. No ale teraz mamy jasność.

To może tak: komunikacja między dwoma maszynami jest czymś zupełnie innym niż współpraca maszyny z człowiekiem. Jeżeli w każdej linijce zapiszemy dane przesyłane w jedną stronę, to w pierwszym przypadku wystarczy coś takiego:

01

02 05

14

23 89

07 09 00

00

Co może oznaczać:

01 - podaj mi wersję swojego oprogramowania

02 05 - oto wersja: 2.05

14 - podaj mi odczyt z wejścia analogowego nr. 4

23 89 - oto odczyt: 0x2389 [mV]

07 09 00 - ustaw stan wyjścia dwustanowego nr. 9 na 0

00 - OK, ustawiłem

I tyle. Same liczby są zupełnie wystarczające, prawda? Maszyny nie muszą być grzeczne, nie muszą używać przecinków ani polskich liter w słowach. Zupełnie spokojnie możesz coś takiego wymyślić na poczekaniu: spisujesz sobie zestaw komend jakie chcesz by Twoje urządzenie (czujnik?) realizowało, przyporządkowujesz im jakieś liczby i ustalasz zestaw argumentów. Po drugiej stronie analizator musi odbierać kolejne znaki/liczby i je interpretować. Oczywiście można i trzeba wprowadzać mechanizmy kontroli poprawności, bo zawsze może po drodze pójść coś źle a nie chcemy, by na skutek błędu w komunikacji urządzenie zrobiło coś głupiego.

Natomiast w przypadku współpracy czujnika z człowiekiem (przez program terminalowy?) co właśnie chyba starałeś się pokazać, liczby nie są fajne bo trudno nam je zapamiętać. Wtedy używamy poleceń tekstowych. To samo co powyżej mogłoby wyglądać tak:

>ver

Sensor driver ver. 2.05

>adcin 4

9.10V

>digout 9,0

>

No i teraz musisz podjąć decyzję co chcesz zrobić i po co. Opisz dokładnie jakie polecenia masz zamiar zaimplementować, co będzie medium komunikacyjnym itp a coś wymyślimy.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Erdi5073, popatrzyłem trochę w kod który wyprodukowałeś i proponuję zacznij od bardzo prostego zadania pomocniczego. Napisz program, który będzie rozpoznawał dwie komendy, np. literke 'a' i 'b'. Po ich odebraniu niech wysyła jakieś przykładowe dane, np. komunikaty "Hello" i "World". Czyli komunikacja wylądałaby np.

> a

< Hello

> b

< World

Oczywiście bez < > (są tylko do pokazania kierunku komunikacji). Jak to zaczniesz pisać to zobaczysz czy na pewno to co teraz napisałeś jest dobrym początkiem do programu który chcesz napisać.

Link do komentarza
Share on other sites

Chcę aby po podłączeniu Mikrokontrolera w terminalu CoolTerm, na wstępnie wyświetliło się coś takiego:

Alkomat#

rezultat

Alkomat#

...

Chciałbym zaimplementować powyższą strukturę, ponad to chciałbym wykorzystywać komendy:

help - wyświetla listę komend

start - wyświetla pomiar napięcia na pinie PA0 (podłączony do czujnika alkoholu) przekonwertowany na np. promile

select unit -zmienia jednostkę np. na procenty, promile, ppm

select level - zmiana poziomu przy którym dioda ma się zapalić

to podstawowe komendy, gdy będę na tym etapie, z resztą powinienem sobie poradzić

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.