ST 퀘스트 이벤트
Quest 7
퀘스트 7/7 - 파이널 퀘스트 작성자 : Telcard

이번 자유공모에서는 STM32MP1 보드에서 공공데이터(OpenAPI)를 활용한 대기 오염 정보 표시 장치를 구현하고자 합니다.

 

이번에 제가 Qt를 접하게 되면서, Qt에 다양한 기능을 제공한다는 것을 파악하였습니다.

 

그래서 최근 관심을 갖고 라즈베리파이를 활용하고자 했던 부분을 STM32MP1으로 대체하여 진행하고자 합니다.

 

Qt에서 HTTP Get Method를 구현하고자 하며, 이를 통해서 공공데이터(OpenAPI)에서 제공하는 기능을 활용하고자 합니다.

원하는 지역을 선택하고, 요청을 하면 수신된 XML 패킷에서 해당 지역의 대기오염 상태를 확인하여,

전광판(UART) or LED 경관 조명(R/G/B LED)에 표현을 하고자 합니다.

(현재 NeoPixel LED BAR를 주문한 상태이지만, 아직 수령 전인 상태!!!)

 

구현 1. 공공데이터 추출하기 (OpenAPI 활용)

국내에서 필요로 하는 정보는 이곳 공공데이터포털( http://www.data.go.kr )에서 입수할 수 있습니다.

회원 가입 후 필요로 하는 정부 기관의 OpenAPI 사용을 요청하면 승인 후 사용이 가능합니다.

공공기관/지자체 등 모든 정보를 받아볼 수 있습니다.

가장 근접하게 볼 수 있는게, 버스 정보 앱을 들 수 있을 듯 합니다.

제가 이번에 신청한 것은 한국환경공단 대기오염정보에 대해서 요청하여, 인증키를 받아서 사용할 수 있게 되었습니다.

( http://openapi.airkorea.or.kr/ )

 

WEB (HTTP Get 방식에 의해) 을 통해서 데이터 요청/응답에 대한 예제

 

이 외에 다양한 언어 환경에 맞게 제공을 하고 있습니다.

 

이를 위해서 Qt에서 제공하는 Network 관련 컴포넌트를 삽입

#include 
#include 
#include 
#include 

 

* Qt를 이용해서 PC 화면에서 동작 테스트 후 보드에 다운로드해 보았지만 한글 부분이 깨지는 상황이 발생했습니다.

한글 폰트가 보드에 없는 문제로 PC에서 사용한 폰트를 보드에 복사해 줌으로 해결이 됨.

https://make.e4ds.com/make/st_board_view.asp?idx=199&t=2

 

PC의 Ubuntu환경에서 Qt 작업 시 기본 폰트를 사용했습니다.

("Ubuntu") 라고 되어 있네요.

그래서 Ubunut에 /usr/share/fonts/truetype 아래에 ubuntu 라는 폴더가 있더군요.

이 폴더를 보드에 /usr/share/fonts/ttf 폴더 아래에 폴더 통째로 복사했습니다.

 

구현 2. XML 파싱

XML 처리를 위해서 Qt에서 제공하는 XML Parse를 활용하여, XML 파싱에 대해서는 아래의 예제를 참고 하였습니다.

http://qt.shoutwiki.com/wiki/QXmlStreamReader_to_parse_XML_in_Qt

 

 

구현 3. IPCC / RPMSG 메시지 전달

Cortex-A7 Core와 M4와 메시지를 상호 주고 받기 위해서 IPCC(OpenAMP) / RPMSG를 활용

STM32MP1 관련 예제로 제공된 소스 중에서 OpenAMP_TTY_echo 예제를 참고해서 구현했습니다.

https://github.com/STMicroelectronics/STM32CubeMP1/tree/master/Projects/STM32MP157C-DK2/Applications/OpenAMP/OpenAMP_TTY_echo

 

시도 이름으로 실시간 데이터 요청을 하고,

패킷 수신 후 해당 측정소 데이터가 존재하면, 화면에 표시하고

미세먼지/초미세먼지 값에 대해서 기준치를 토대로 하여 4단계의 상황을 구분

 

구현 4. RED 구현

Cortex-A7에서 수신된 패킷을 파싱 후 미세먼지 상태 정보를 Cortex-M4에 전달하면,

메시지를 받아서 R/G/B LED 구동

 

NeoPixel LED를 구동하기 위해서는 NRZ 인코딩 신호를 만들어야 하는데,

SPI를 이용해서 1bit 데이터에 대해서 4bit clock으로 구성하여 만들면 되는 것으로 파악되었다.

(참고 사이트 : https://rs29.tistory.com/1 )

 

다만, 실제 LED BAR 입고가 다음주 이후에 가능한 상황에서 테스트가 어려워서,

우선적으로 보드에 달려있는 BLUE / ORANGE / RED LED를 이용하였습니다.

미세먼지 or 초미세먼지 중에서 높은 값에 대해서 반응하도록 하였다.

1단계 (좋음) : BLUE

2단계 (보통) : BLUE + ORANGE

3단계 (나쁨) : ORANGE + RED

4단계 (매우나쁨) : RED

 

Qt를 이용한 화면 구성 및 동작 화면

 

 

실제 동작 영상

Cortex-A7 응용 프로그램 코드 (mainwindow.cpp)

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include 
#include 
#include 
#include 
#include 

#include 
#include 

/// 미세먼지 좋음 30 / 보통 80 / 나쁨 150 / 매우나쁨 151
///
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    urlHead = "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureSidoLIst?serviceKey=서비스키&numOfRows=50&pageNo=1&sidoName=";
    urlTail = "&searchCondition=HOUR&";
    //url = "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty?ServiceKey=sZ7n9YMCZFcbjnHs414LbACoHcfAkqLid%2BUlFPBdtTJqtJCc1o6QrZF%2FdegPTN%2Bb1fFdaaXThYgg9wqLWumhwg%3D%3D&numOfRows=10&pageNo=1&stationName=종로구&dataTerm=DAILY&ver=1.3";
    itemSeoul = "강남구,강동구,강북구,강서구,관악구,광진구,구로구,금천구,노원구,도봉구,\
동대문구,동작구,마포구,서대문구,서초구,성동구,성북구,송파구,양천구,영등포구,\
용산구,은평구,종로구,중구,중랑구";
    itemBusan = "강서구,금정구,기장군,남구,동구,동래구,부산진구,북구,사상구,사하구,서구,\
수영구,연제구,영도구,중구,해운대구";
    itemGwangju = "광산구,남구,동구,북구,서구";

//    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
//    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(httpreplyFinished(QNetworkReply*)));

    //connect(ui->btnQuit, SIGNAL(clicked()), this, SLOT(close()));
    //connect(ui->cbCityName, SIGNAL(clicked(int index)), this, SLOT(on_cbCityName_activated(int index)));

    fd = open("/dev/ttyRPMSG0", O_RDWR);

    ui->btnRequest->setEnabled(true);
    ui->btnCancel->setEnabled(false);
    ui->btnQuit->setEnabled(true);

    this->on_cbCityName_activated(0);
}

void MainWindow::httpReadyRead()
{
    // this slot gets called every time the QNetworkReply has new data.
    // We read all of its new data and write it into the file.
    // That way we use less RAM than when reading it at the finished()
    // signal of the QNetworkReply
    rcvPacket += reply->readAll();

    ///?ui->txtResponse->setText(rcvPacket);
}

void MainWindow::parsingPacket()
{
    QXmlStreamReader reader(rcvPacket);
    QString inputData;
    QList< QMap > env;

    while(!reader.atEnd() && !reader.hasError())
    {
        QXmlStreamReader::TokenType token = reader.readNext();

        if (token == QXmlStreamReader::StartDocument)
        {
            continue;
        }

        /* If token is StartElement, we'll see if we can read it.*/
        if(token == QXmlStreamReader::StartElement) {
            /* If it's named persons, we'll go to the next.*/
            if(reader.name() == "items") {
                continue;
            }

            /* If it's named person, we'll dig the information from there.*/
            if(reader.name() == "item") {
                env.append(this->parseEnv(reader));
            }
        }
    } // while

    /* Error handling. */
    if(reader.hasError()) {
        QMessageBox::critical(this,
                              "QXSRExample::parseXML",
                              reader.errorString(),
                              QMessageBox::Ok);
    }
    /* Removes any device() or data from the reader
     * and resets its internal state to the initial state. */
    reader.clear();

    //ui->txtResponse->setText(env.);
//    this->addEnvItemtoUI(env);
//    for (int i = 0; i < env.size(); i++)

    bool flag = false;
    foreach (auto item, env)
    {
        if (item["cityName"] == stationName)
        {
            ui->txtso2Value->setText(item["so2Value"]);
            ui->txtcoValue->setText(item["coValue"]);
            ui->txto3Value->setText(item["o3Value"]);
            ui->txtno2Value->setText(item["no2Value"]);
            ui->txtpm10Value->setText(item["pm10Value"]);
            ui->txtpm25Value->setText(item["pm25Value"]);

            int pm10 = item["pm10Value"].toInt();
            int pm25 = item["pm25Value"].toInt();

            int state = 0;

            if ((pm10 >= 151) || (pm25 >= 151)) state = 4;
            else if ((pm10 >= 81) || (pm25 >= 81)) state = 3;
            else if ((pm10 >= 31) || (pm25 >= 31)) state = 2;
            else if ((pm10 >= 0) || (pm25 >= 0)) state = 1;
//
            char msg[20];
            sprintf(msg, "$RPMSG,%1d\r\n", state);
            write(fd, msg, strlen(msg));

            flag = true;
            break;
        }
    }
    if (flag == false)
    {
        ui->txtResponse->setText("No response");
    }
}

QMap MainWindow::parseEnv(QXmlStreamReader& xml)
{
    QMap envItem;

    /* Let's check that we're really getting a person. */
    if(xml.tokenType() != QXmlStreamReader::StartElement &&
            xml.name() == "item") {
        return envItem;
    }

    /* Let's get the attributes for person */
    QXmlStreamAttributes attributes = xml.attributes();

    /* Next element... */
    xml.readNext();
    /*
     * We're going to loop over the things because the order might change.
     * We'll continue the loop until we hit an EndElement named person.
     */
    while(!(xml.tokenType() == QXmlStreamReader::EndElement &&
            xml.name() == "item"))
    {
        if(xml.tokenType() == QXmlStreamReader::StartElement) {
            /* We've found first name. */
            if(xml.name() == "dataTime") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found surname. */
            if(xml.name() == "cityName") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found email. */
            if(xml.name() == "so2Value") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found website. */
            if(xml.name() == "coValue") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found website. */
            if(xml.name() == "o3Value") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found website. */
            if(xml.name() == "no2Value") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found website. */
            if(xml.name() == "pm10Value") {
                this->addElementDataToMap(xml, envItem);
            }
            /* We've found website. */
            if(xml.name() == "pm25Value") {
                this->addElementDataToMap(xml, envItem);
            }
        }
        /* ...and next... */
        xml.readNext();
    }
    return envItem;
}

void MainWindow::addElementDataToMap(QXmlStreamReader& xml,
                                      QMap& map) const {
    /* We need a start element, like  */
    if(xml.tokenType() != QXmlStreamReader::StartElement) {
        return;
    }
    /* Let's read the name... */
    QString elementName = xml.name().toString();
    /* ...go to the next. */
    xml.readNext();
    /*
     * This elements needs to contain Characters so we know it's
     * actually data, if it's not we'll leave.
     */
    if(xml.tokenType() != QXmlStreamReader::Characters) {
        return;
    }
    /* Now we can add it to the map.*/
    map.insert(elementName, xml.text().toString());
}

// When download finished or canceled, this will be called
void MainWindow::httpDownloadFinished()
{
    // when canceled
    if (httpRequestAborted) {

        reply->deleteLater();

        return;
    }

    // get redirection url
    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (reply->error()) {
        QMessageBox::information(this, tr("HTTP"),
                                 tr("Download failed: %1.")
                                 .arg(reply->errorString()));
        ui->btnRequest->setEnabled(true);
    } else if (!redirectionTarget.isNull()) {
        QUrl newUrl = url.resolved(redirectionTarget.toUrl());
        if (QMessageBox::question(this, tr("HTTP"),
                                  tr("Redirect to %1 ?").arg(newUrl.toString()),
                                  QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
            url = newUrl;
            reply->deleteLater();

            startRequest(url);
            return;
        }
    } else {
        ui->btnRequest->setEnabled(true);
        // XML Parsing
        this->parsingPacket();

    }

    ui->btnCancel->setEnabled(false);

    reply->deleteLater();
    reply = 0;
    manager = 0;
}

// This will be called when download button is clicked
void MainWindow::startRequest(QUrl url)
{
    // get() method posts a request
    // to obtain the contents of the target request
    // and returns a new QNetworkReply object
    // opened for reading which emits
    // the readyRead() signal whenever new data arrives.
    reply = manager->get(QNetworkRequest(url));

    // Whenever more data is received from the network,
    // this readyRead() signal is emitted
    connect(reply, SIGNAL(readyRead()),
            this, SLOT(httpReadyRead()));

    // This signal is emitted when the reply has finished processing.
    // After this signal is emitted,
    // there will be no more updates to the reply's data or metadata.
    connect(reply, SIGNAL(finished()),
            this, SLOT(httpDownloadFinished()));
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_cbCityName_activated(int index)
{
    QStringList list;

    if (index == 0)
    {
        // 서울
        ui->cbStationName->clear();

        list = itemSeoul.split(",");
        ui->cbStationName->addItems(list);
        //ui->cbStationName

    } else if (index == 1)
    {
        // 부산
        ui->cbStationName->clear();
        list = itemBusan.split(",");
        ui->cbStationName->addItems(list);

    } else if (index == 2)
    {
        // 광주
        ui->cbStationName->clear();
        list = itemGwangju.split(",");
        ui->cbStationName->addItems(list);

    } else
    {
        // 기본 : 서울
    }
}

void MainWindow::on_btnRequest_clicked()
{
    manager = new QNetworkAccessManager(this);

    ui->btnRequest->setEnabled(false);
    ui->btnCancel->setEnabled(true);
    ui->txtResponse->setText(NULL);
    rcvPacket = "";

// update url : with city name
    cityName = ui->cbCityName->currentText();
    stationName = ui->cbStationName->currentText();

    url = urlHead + cityName + urlTail;

    // schedule the request
    httpRequestAborted = false;
    startRequest(url);
}

void MainWindow::on_btnCancel_clicked()
{
    httpRequestAborted = true;
    reply->abort();
    ui->btnRequest->setEnabled(true);
    ui->btnCancel->setEnabled(false);
}

void MainWindow::on_btnQuit_clicked()
{

    // close /dev/rpmsg0
    ::close(fd);

    this->close();
}

 

Cortex-M4 응용 프로그램 코드

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * 

 

© Copyright (c) 2020 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "openamp.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define MAX_BUFFER_SIZE RPMSG_BUFFER_SIZE /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ IPCC_HandleTypeDef hipcc; SPI_HandleTypeDef hspi4; DMA_HandleTypeDef hdma_spi4_tx; /* USER CODE BEGIN PV */ VIRT_UART_HandleTypeDef huart0; //VIRT_UART_HandleTypeDef huart1; __IO FlagStatus VirtUart0RxMsg = RESET; uint8_t VirtUart0ChannelBuffRx[MAX_BUFFER_SIZE]; uint16_t VirtUart0ChannelRxSize = 0; //__IO FlagStatus VirtUart1RxMsg = RESET; //uint8_t VirtUart1ChannelBuffRx[MAX_BUFFER_SIZE]; //uint16_t VirtUart1ChannelRxSize = 0; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); void PeriphCommonClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_IPCC_Init(void); static void MX_SPI4_Init(void); int MX_OPENAMP_Init(int RPMsgRole, rpmsg_ns_bind_cb ns_bind_cb); /* USER CODE BEGIN PFP */ void LED_Drive(uint32_t mode); void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart); //void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void LED_Drive(uint32_t mode) { if (mode == 1) { // BLUE HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET); HAL_Delay(50); } else if (mode == 2) { // BLUE & ORANGe HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET); HAL_Delay(50); } else if (mode == 3) { // ORANGE & RED HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET); HAL_Delay(50); } else if (mode == 4) { // RED HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_SET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_SET); HAL_Delay(50); } else { HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(LED_Y_GPIO_Port, LED_Y_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(PA13_GPIO_Port, PA13_Pin, GPIO_PIN_SET); } } /* 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 */ if(IS_ENGINEERING_BOOT_MODE()) { /* Configure the system clock */ SystemClock_Config(); } if(IS_ENGINEERING_BOOT_MODE()) { /* Configure the peripherals common clocks */ PeriphCommonClock_Config(); } /* IPCC initialisation */ MX_IPCC_Init(); /* OpenAmp initialisation ---------------------------------*/ MX_OPENAMP_Init(RPMSG_REMOTE, NULL); /* USER CODE BEGIN SysInit */ /*HW semaphore Clock enable*/ __HAL_RCC_HSEM_CLK_ENABLE(); /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_SPI4_Init(); /* USER CODE BEGIN 2 */ log_info("Virtual UART0 OpenAMP-rpmsg channel creation\r\n"); if (VIRT_UART_Init(&huart0) != VIRT_UART_OK) { log_err("VIRT_UART_Init UART0 failed.\r\n"); Error_Handler(); } // log_info("Virtual UART1 OpenAMP-rpmsg channel creation\r\n"); // if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) { // log_err("VIRT_UART_Init UART1 failed.\r\n"); // Error_Handler(); // } /*Need to register callback for message reception by channels*/ if(VIRT_UART_RegisterCallback(&huart0, VIRT_UART_RXCPLT_CB_ID, VIRT_UART0_RxCpltCallback) != VIRT_UART_OK) { Error_Handler(); } // if(VIRT_UART_RegisterCallback(&huart1, VIRT_UART_RXCPLT_CB_ID, VIRT_UART1_RxCpltCallback) != VIRT_UART_OK) // { // Error_Handler(); // } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ int mode; LED_Drive(0); while (1) { OPENAMP_check_for_message(); if (VirtUart0RxMsg) { VirtUart0RxMsg = RESET; VIRT_UART_Transmit(&huart0, VirtUart0ChannelBuffRx, VirtUart0ChannelRxSize); if ( (VirtUart0ChannelBuffRx[0] == '$') && (VirtUart0ChannelBuffRx[1] == 'R') && (VirtUart0ChannelBuffRx[2] == 'P') && (VirtUart0ChannelBuffRx[3] == 'M') && (VirtUart0ChannelBuffRx[4] == 'S') && (VirtUart0ChannelBuffRx[5] == 'G') ) mode = VirtUart0ChannelBuffRx[7] - '0'; } if (mode != 0) LED_Drive(mode); /* USER CODE END WHILE */ /* 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}; /** Configure LSE Drive Capability */ HAL_PWR_EnableBkUpAccess(); __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMHIGH); /** Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI |RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS_DIG; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = 16; RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL2.PLLSource = RCC_PLL12SOURCE_HSE; RCC_OscInitStruct.PLL2.PLLM = 3; RCC_OscInitStruct.PLL2.PLLN = 66; RCC_OscInitStruct.PLL2.PLLP = 2; RCC_OscInitStruct.PLL2.PLLQ = 1; RCC_OscInitStruct.PLL2.PLLR = 1; RCC_OscInitStruct.PLL2.PLLFRACV = 0x1400; RCC_OscInitStruct.PLL2.PLLMODE = RCC_PLL_FRACTIONAL; RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL3.PLLSource = RCC_PLL3SOURCE_HSE; RCC_OscInitStruct.PLL3.PLLM = 2; RCC_OscInitStruct.PLL3.PLLN = 34; RCC_OscInitStruct.PLL3.PLLP = 2; RCC_OscInitStruct.PLL3.PLLQ = 17; RCC_OscInitStruct.PLL3.PLLR = 37; RCC_OscInitStruct.PLL3.PLLRGE = RCC_PLL3IFRANGE_1; RCC_OscInitStruct.PLL3.PLLFRACV = 6660; RCC_OscInitStruct.PLL3.PLLMODE = RCC_PLL_FRACTIONAL; RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL4.PLLSource = RCC_PLL4SOURCE_HSE; RCC_OscInitStruct.PLL4.PLLM = 4; RCC_OscInitStruct.PLL4.PLLN = 99; RCC_OscInitStruct.PLL4.PLLP = 6; RCC_OscInitStruct.PLL4.PLLQ = 8; RCC_OscInitStruct.PLL4.PLLR = 8; RCC_OscInitStruct.PLL4.PLLRGE = RCC_PLL4IFRANGE_0; RCC_OscInitStruct.PLL4.PLLFRACV = 0; RCC_OscInitStruct.PLL4.PLLMODE = RCC_PLL_INTEGER; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** RCC Clock Config */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2 |RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4 |RCC_CLOCKTYPE_PCLK5; RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_PLL2; RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1; RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_PLL3; RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1; RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV2; RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV4; RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV2; RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV2; RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK) { Error_Handler(); } /** Set the HSE division factor for RTC clock */ __HAL_RCC_RTC_HSEDIV(24); } /** * @brief Peripherals Common Clock Configuration * @retval None */ void PeriphCommonClock_Config(void) { RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the common periph clock */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_CKPER; PeriphClkInit.CkperClockSelection = RCC_CKPERCLKSOURCE_HSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /** * @brief IPCC Initialization Function * @param None * @retval None */ static void MX_IPCC_Init(void) { /* USER CODE BEGIN IPCC_Init 0 */ /* USER CODE END IPCC_Init 0 */ /* USER CODE BEGIN IPCC_Init 1 */ /* USER CODE END IPCC_Init 1 */ hipcc.Instance = IPCC; if (HAL_IPCC_Init(&hipcc) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN IPCC_Init 2 */ /* USER CODE END IPCC_Init 2 */ } /** * @brief SPI4 Initialization Function * @param None * @retval None */ static void MX_SPI4_Init(void) { /* USER CODE BEGIN SPI4_Init 0 */ /* USER CODE END SPI4_Init 0 */ /* USER CODE BEGIN SPI4_Init 1 */ /* USER CODE END SPI4_Init 1 */ /* SPI4 parameter configuration*/ hspi4.Instance = SPI4; hspi4.Init.Mode = SPI_MODE_MASTER; hspi4.Init.Direction = SPI_DIRECTION_2LINES; hspi4.Init.DataSize = SPI_DATASIZE_8BIT; hspi4.Init.CLKPolarity = SPI_POLARITY_LOW; hspi4.Init.CLKPhase = SPI_PHASE_1EDGE; hspi4.Init.NSS = SPI_NSS_SOFT; hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi4.Init.TIMode = SPI_TIMODE_DISABLE; hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi4.Init.CRCPolynomial = 0x0; hspi4.Init.NSSPMode = SPI_NSS_PULSE_ENABLE; hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN; hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE; hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE; hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE; hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE; if (HAL_SPI_Init(&hspi4) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SPI4_Init 2 */ /* USER CODE END SPI4_Init 2 */ } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMAMUX_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA interrupt init */ /* DMA2_Stream0_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); /*Configure GPIO pin : PA13_Pin */ GPIO_InitStruct.Pin = PA13_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(PA13_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : LED_Y_Pin */ GPIO_InitStruct.Pin = LED_Y_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_Y_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : LED_B_Pin */ GPIO_InitStruct.Pin = LED_B_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_B_GPIO_Port, &GPIO_InitStruct); } /* USER CODE BEGIN 4 */ void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart) { log_info("Msg received on VIRTUAL UART0 channel: %s \n\r", (char *) huart->pRxBuffPtr); /* copy received msg in a variable to sent it back to master processor in main infinite loop*/ VirtUart0ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1; memcpy(VirtUart0ChannelBuffRx, huart->pRxBuffPtr, VirtUart0ChannelRxSize); VirtUart0RxMsg = SET; } //void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart) //{ // // log_info("Msg received on VIRTUAL UART1 channel: %s \n\r", (char *) huart->pRxBuffPtr); // // /* copy received msg in a variable to sent it back to master processor in main infinite loop*/ // VirtUart1ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1; // memcpy(VirtUart1ChannelBuffRx, huart->pRxBuffPtr, VirtUart1ChannelRxSize); // VirtUart1RxMsg = SET; //} /* 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 can add his own implementation to report the HAL error return state */ /* 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 can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/