В начало | Зарегистрироваться | Заказать наши киты почтой
 
 
 
 

Обмен данными между Android-приложением и nRF52832. Часть 1. Serial

📆13 апреля 2023   ✒️ayan   🔎683   💬4  
Обмен данными между Android-приложением и nRF52832. Часть 1. Serial

Здравствуйте! Представляю вашему вниманию проект обмена данными между Android-приложением и SoC nRF52832 через последовательный порт.

Видео-демонстрация проекта




Код для nRF52832

В проекте использована макетная плата nRF52832, в состав которой входят светодиод (вывод P0.17) и кнопка (вывод P0.14).

Проект, кроме main.c, содержит три библиотеки.
Библиотека Serial обеспечивает:
• инициализацию протокола UART со скоростью 9600 bps и разрешение прерывания по факту приёма данных,
• отправку байта данных,
• приём байта данных с сохранением в переменной receivedData;
Serial.h
#ifndef SERIAL_H_
#define SERIAL_H_

#include "nrf.h"

#define TX_PIN    6
#define RX_PIN    8

void serialBegin();
void serialSendByte(char data);

extern volatile unsigned char receivedData;

#endif /* SERIAL_H_ */

Serial.c
#include "Serial.h"

volatile unsigned char receivedData = 0;

void UART0_IRQHandler()
{
  while(!NRF_UART0->EVENTS_RXDRDY);
  NRF_UART0->EVENTS_RXDRDY = 0;  
  receivedData = NRF_UART0->RXD;
}  

void serialBegin()
{
  NRF_GPIO->PIN_CNF[TX_PIN] = GPIO_PIN_CNF_DIR_Output << GPIO_DIR_PIN11_Pos;
  NRF_UART0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600 << UART_BAUDRATE_BAUDRATE_Pos;
  NRF_UART0->PSELTXD = TX_PIN;   
  NRF_UART0->PSELRXD = RX_PIN;   
  NRF_UART0->INTENSET = UART_INTENSET_RXDRDY_Enabled << UART_INTENSET_RXDRDY_Pos;    
  NVIC_EnableIRQ(UARTE0_UART0_IRQn);    
  NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled << UART_ENABLE_ENABLE_Pos;
  NRF_UART0->TASKS_STARTTX = 1UL << 0;
  NRF_UART0->TASKS_STARTRX = 1UL << 0;  
}

void serialSendByte(char data)
{
  NRF_UART0->TXD = data;   
  while(!NRF_UART0->EVENTS_TXDRDY);
  NRF_UART0->EVENTS_TXDRDY = 0;   
}

В с-файле библиотеки button настраивается прерывание по обоим фронтам сигнала на входе P0.14, в обработчике которого :
• устанавливается в 1 значение переменной buttonFlag,
• прерывание запрещается до истечения времени дребезга кнопки.

В файле button.h оформлены макросы:
• разрешения/запрета прерывания,
• опроса регистра IN с целью определить, отпущена ли кнопка.
button.h
#ifndef BUTTON_H_
#define BUTTON_H_

#include "nrf.h"

#define BUTTON                  14
#define BUTTON_RELEASED         NRF_GPIO->IN & (1UL << BUTTON)
#define BUTTON_ON               NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Enabled << GPIOTE_INTENSET_IN0_Pos;
#define BUTTON_OFF              NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN0_Disabled << GPIOTE_INTENSET_IN0_Pos;
#define BUTTON_BOUNCE_TIME      30

void buttonInit();

extern volatile unsigned char buttonFlag;

#endif /* BUTTON_H_ */

button.c
#include "button.h"

volatile unsigned char buttonFlag = 0;

void GPIOTE_IRQHandler()
{
    while(!NRF_GPIOTE->EVENTS_IN[0]);    
    NRF_GPIOTE->EVENTS_IN[0] = 0;
    BUTTON_OFF;
    buttonFlag = 1;
}

void buttonInit()
{
    NRF_GPIO->PIN_CNF[BUTTON] = (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos);
    NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) |
                            (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) |
                            (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos) |
                            (BUTTON << GPIOTE_CONFIG_PSEL_Pos);
    BUTTON_ON;
    NVIC_EnableIRQ(GPIOTE_IRQn);                         
}

В файле main.c:
1. Функция systemInit() настраивает таймер TIMER0 на генерацию прерывания раз в миллисекунду. В обработчике прерывания устанавливается в 1 значение переменной clockFlag.
2. В основной функции осуществляется начальная инициализация модулей МК, а затем в цикле опрашиваются значения переменных receivedData и clockFlag и:
а) Включается/выключается светодиод при значениях „1“/“2“ переменной receivedData, соответственно. При этом, сама переменная обнуляется.
б) Один раз в миллисекунду проверяется значение buttonFlag и, если оно равно 1, инкрементируется переменная clockCounter, отсчитывающая время дребезга (30 мс). По истечению последнего buttonFlag и clockCounter обнуляются и в зависимости от текущего состояния кнопки (нажата/отпущена) в последовательный порт отправляется соответствующий символ (“1”/”2”).
main.c
#include "Serial.h"
#include "led.h"
#include "button.h"

volatile unsigned char clockFlag = 0;
unsigned char clockCounter = 0;

void TIMER0_IRQHandler()
{
  NRF_TIMER0->EVENTS_COMPARE[0] = 0; 
  clockFlag = 1;
}

void SystemInit()
{
  NRF_TIMER0->PRESCALER = 6; 
  NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_08Bit << TIMER_BITMODE_BITMODE_Pos;
  NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
  NVIC_EnableIRQ(TIMER0_IRQn);
  NRF_TIMER0->TASKS_START = 1;
}

int main(void)
{
  serialBegin();
  ledInit();
  buttonInit();

  while (1)
  {
    if(receivedData)
    {
      if(receivedData == '1')
        LED_ON;
      else if(receivedData == '2')
        LED_OFF;        

      receivedData = 0;  
    }
    if(clockFlag)
    {
      clockFlag = 0;
      if(buttonFlag)
      {
        clockCounter++;
        if(clockCounter == BUTTON_BOUNCE_TIME)
        {
          clockCounter = 0;
          buttonFlag = 0;
          if(BUTTON_RELEASED)
            serialSendByte('2');
          else
            serialSendByte('1');          
          BUTTON_ON;  
        }
      }
    }
  }
}

Android-приложение

Приложение написано на языке Java в Android Studio.

В файле xml-файле приложения прописаны параметры элементов графического интерфейса — кнопок соединения с последовательным портом и включения/выключения светодиода, а также текстового поля, отражающего состояние кнопки на макетной плате nRF52832.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/connectBtn"
        android:layout_width="100dp"
        android:layout_height="80dp"
        android:onClick="openSerialPort"
        android:text="connect"
        android:textSize="30px"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.05"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.023" />

    <Button
        android:id="@+id/ledOnBtn"
        android:layout_width="100dp"
        android:layout_height="80dp"
        android:onClick="ledOn"
        android:text="led on"
        android:textSize="30px"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.024" />

    <Button
        android:id="@+id/ledOffBtn"
        android:layout_width="100dp"
        android:layout_height="80dp"
        android:onClick="ledOff"
        android:text="led off"
        android:textSize="30px"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.949"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.023" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="366dp"
        android:layout_height="64dp"
        android:onClick="ledOff"
        android:text="Button is released"
        android:textAlignment="center"
        android:textColor="#143215"
        android:textSize="110px"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.488"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.215" />

</androidx.constraintlayout.widget.ConstraintLayout>

В MainActivity Java-файла оформлены обработчики нажатия кнопок графического интерфейса:
1. ledOn() передаёт в порт символ “1”,
2. ledOff() передаёт в порт символ “2”,
3. openSerialPort():
• обеспечивает соединение с последовательным портом на скорости 9600 bps,
• запускает отдельный поток, в котором при поступлении от nRF52832 символов “1” или “2” соответствующим образом меняется содержимое текстового поля.
MainActivity.java
package com.example.androidserial;

import androidx.appcompat.app.AppCompatActivity;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private UsbManager mUsbManager;
    private UsbSerialPort mSerialPort;
    private UsbDeviceConnection mConnection;
    private UsbSerialDriver driver;
    private Button connnect, ledOnBtn, ledOffBtn;
    private TextView mTextView;
    private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";

    private enum UsbPermission { Unknown, Requested, Granted, Denied }

    private UsbPermission usbPermission = UsbPermission.Unknown;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        connnect = findViewById(R.id.connectBtn);
        ledOnBtn = findViewById(R.id.ledOnBtn);
        ledOffBtn = findViewById(R.id.ledOffBtn);
        mTextView = findViewById(R.id.textView);
        mTextView.setTextColor(Color.BLACK);
    }

    public void openSerialPort(View view) {
        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(mUsbManager);
        if (availableDrivers.isEmpty()) {
            Toast.makeText(this, "No serial device found!", Toast.LENGTH_SHORT).show();
        } else {
            driver = availableDrivers.get(0);
            mSerialPort = driver.getPorts().get(0);
            mConnection = mUsbManager.openDevice(driver.getDevice());
        }

        if (mConnection == null && usbPermission == UsbPermission.Unknown && !mUsbManager.hasPermission(driver.getDevice())) {
            usbPermission = UsbPermission.Requested;
            int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0;
            PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(INTENT_ACTION_GRANT_USB), flags);
            mUsbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
            return;
        }

        try {
            mSerialPort.open(mConnection);
            mSerialPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
            Toast.makeText(this, "connected", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Toast.makeText(this, "Error opening device: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }

        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        byte[] buffer = new byte[3];
                        mSerialPort.read(buffer, 100);
                        if (buffer[0] == '1') {
                            mTextView.setText("Button is pressed");
                        } else if (buffer[0] == '2') {
                            mTextView.setText("Button is released");
                        }
                    } catch (IOException e) {

                    }
                }
            }
        }).start();
    }

    public void ledOn(View view) {
        try {
            byte[] data = "1".getBytes();
            mSerialPort.write(data, 2000);
        } catch (Exception e) {
            Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
        }
    }

    public void ledOff(View view) {
        try {
            byte[] data = "2".getBytes();
            mSerialPort.write(data, 2000);
        } catch (Exception e) {
            Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
        }
    }
}


Файлы

🎁Все коды и проект из статьи в zip  19.03 Mb ⇣ 9

Благодарю за внимание!
Продожнение следует.

Камрад, рассмотри датагорские рекомендации

🌼 Полезные и проверенные железяки, можно брать

Опробовано в лаборатории редакции или читателями.




 

Читательское голосование

Нравится

Статью одобрили 9 читателей.

Для участия в голосовании зарегистрируйтесь и войдите на сайт с вашими логином и паролем.
 

Поделись с друзьями!

 

 

Связанные материалы

 

Схема на Датагоре. Новая статья Электронные часы-термометр с беспроводным датчиком через радиомодуль nRF24L01... Здравствуйте, уважаемые Датагорцы! Представляю вашему вниманию электронные часы с функцией...
Схема на Датагоре. Новая статья Обмен данными между Java-приложением и МК. Часть 1. По проводу, USB-UART... Приветствую всех жителей и гостей кибер-города Датагор! Работа устройства на базе микроконтроллера...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 3. TFT дисплей 2.8" (240х320) на ILI9341... Битва за урожай закончена, можно продолжить повествование. Полноцветный TFT-дисплей 240×320 ILI9341...
Схема на Датагоре. Новая статья Микроконтроллер управляет дисплеем от телефона Siemens!... Многие радиолюбители собирают для себя какие-то конструкции. Иногда простейшие, иногда очень...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК.... Сегодня мы рассмотрим работу следующих модулей периферии: • порта ввода-вывода, • таймера •...
Схема на Датагоре. Новая статья Обмен данными между Java-приложением и МК. Часть 2. По воздуху, Bluetooth... Привет всем датагорчанам-любителям МК! Сегодня мы рассмотрим двусторонний обмен между...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 4. Система адресации памяти, назначение выводов, тактирование и прерывания МК... Привет датагорцам! Сегодня мы остановимся на следующих вопросах касательно рассматриваемых нами...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 6. Протоколы обмена данными I2C и SPI... В проекте из предыдущей части нашей ассемблерной эпопеи мы подключали к микроконтроллеру светодиод...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 7. Компиляция, отладка, загрузка... Привет датагорцам и гостям нашего кибер-города! В предыдущих частях материала по Ассемблеру...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 2. Шаблонные файлы и инструкции МК... В предыдущей части статьи мы провели подготовительную работу и вкратце разобрали принципы работы...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 5. Графика... В предыдущих частях статьи нами более или менее подробно были рассмотрены основные принципы работы...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 3. Макросы и функции... Привет, датагорцы — любители Ассемблера! В пункте 2.5.2 «Инструкции условного перехода» предыдущей...
 

Комментарии, вопросы, ответы, дополнения, отзывы

 

<
Читатель Датагора

error

<
Читатель Датагора

Datagor

<
Читатель Датагора

erbol

<
Читатель Датагора

Datagor

Добавить комментарий, вопрос, отзыв 💬

Камрады, будьте дружелюбны, соблюдайте правила!

  • Смайлы и люди
    Животные и природа
    Еда и напитки
    Активность
    Путешествия и места
    Предметы
    Символы
    Флаги
 
 
В начало | Зарегистрироваться | Заказать наши киты почтой