segunda-feira, 10 de junho de 2024

MILK-V WIRINGx - Introdução - Roda on Duo, Duo 256 e DuoS

Introdução

WiringX é uma biblioteca que permite aos desenvolvedores controlar o GPIO de diversas plataformas com funções genéricas e uniformes. Ao usar o WiringX, o mesmo código será executado em todas as plataformas suportadas pelo WiringX, nativamente.

Este artigo será dividido nas quatro partes a seguir para apresentar como desenvolver aplicações no Duo usando o wireingX:

  1. APIs de WiringX
  2. Demonstração de código de uso básico
  3. Configuração do ambiente de compilação da aplicação baseado em WiringX
  4. Introdução a algumas demonstrações e projetos implementados usando WiringX

Se você já está familiarizado com o uso do WiringX, pode consultar diretamente nosso código de exemplo: duo-examples

Observe que muitas funções de pinos da série Duo são multiplexadas. Ao usar wiringX para controlar as funções de cada pino, é importante confirmar o estado atual do pino para garantir que ele corresponda à funcionalidade desejada. Caso contrário, você pode usar o comando duo-pinmux para mudar para a função desejada.

Consulte as instruções de uso detalhadas para obter mais informações: pinmux .

Números do WiringX Duo/Duo256

Os números dos pinos do WiringX do Duo e Duo256M são consistentes com os números dos nomes dos pinos. No entanto, o pino de controle do LED azul não está disponível na pinagem física de 40 pinos e o número do pino do WiringX é 25.

Imagens de documentos

Números do WiringX DuoS

Os números dos pinos do WiringX do DuoS são consistentes com os números dos pinos físicos. O pino de controle do LED azul não está nos pinos físicos de 40PIN e seu número de WiringX é 0.

Imagens de documentos

HEADER J3

GPIO Header J3 usa níveis lógicos de 3,3 V.

wiringXPIN NAMEPIN#PIN#PIN NAMEwiringX
3V3
1
2
VSYS(5V)
3B20
3
4
VSYS(5V)
5B21
5
6
GND
7B18
7
8
A168
GND*
9
10
A1710
11B11
11
12
B1912
13B12
13
14
GND
15B22
15
16
A2016
3V3
17
18
A1918
19B13
19
20
GND
21B14
21
22
A1822
23B15
23
24
B1624
GND
25
26
A2826

GND*: O pino 9 é um GPIO de baixo nível na versão V1.1 do hardware e é GND na versão V1.2 e posterior.

HEADER J4

GPIO Header J4 usa níveis lógicos de 1,8V.

wiringXPIN NAMEPIN#PIN#PIN NAMEwiringX
VSYS(5V)
52
51
AUDIO_OUT_R
50B1
50
49
AUDIO_OUT_L
48B2
48
47
AUDIO_IN_R
46B3
46
45
AUDIO_IN_L
44E2
44
43
3V3
42E1
42
41
C1841
40E0
40
39
C1939
GND
38
37
GND
36C20
36
35
C1635
34C21
34
33
C1733
GND
32
31
GND
30C14
30
29
C1229
28C15
28
27
C1327

A maioria dos pinos neste conector possui funções dedicadas, como sinais MIPI DSI, sinais de tela sensível ao toque e sinais de áudio. Se não houver nenhum requisito especial, não é recomendado usar os pinos deste cabeçalho como GPIO.

1. CÓDIGO EXEMPLO

Exemplo  de uso de GPIO

Aqui está um exemplo de trabalho com GPIO. Ele alternará o pino 20 no Duo a cada 1 segundo, puxando-o para HIGH e depois para LOW. O número do pino físico para o pino 20 está 15 no sistema de numeração WiringX.

#include <stdio.h>
#include <unistd.h>

#include <wiringx.h>

int main() {
int DUO_GPIO = 15;

// Duo: milkv_duo
// Duo256M: milkv_duo256m
// DuoS: milkv_duos
if(wiringXSetup("milkv_duo", NULL) == -1) {
wiringXGC();
return -1;
}

if(wiringXValidGPIO(DUO_GPIO) != 0) {
printf("Invalid GPIO %d\n", DUO_GPIO);
}

pinMode(DUO_GPIO, PINMODE_OUTPUT);

while(1) {
printf("Duo GPIO (wiringX) %d: High\n", DUO_GPIO);
digitalWrite(DUO_GPIO, HIGH);
sleep(1);
printf("Duo GPIO (wiringX) %d: Low\n", DUO_GPIO);
digitalWrite(DUO_GPIO, LOW);
sleep(1);
}

return 0;
}

Após compilar e rodar no Duo, você pode usar um multímetro ou osciloscópio para medir o estado do pino 20 e verificar se ele corresponde ao comportamento esperado.

Você também pode usar o pino do LED integrado para verificar o comportamento. Observando o estado ligado/desligado do LED, você pode determinar intuitivamente se o programa está sendo executado corretamente. O número do pino WiringX para o LED é 25. Simplesmente modifique o código mencionado acima substituindo pin 15 por pin 25. No entanto, observe que o firmware padrão possui um script para controlar o piscar do LED na inicialização, que precisa ser desativado. Você pode consultar o exemplo de explicação para blink abaixo para obter instruções sobre como desativá-lo.

Exemplo de uso I2C

Aqui está um exemplo de comunicação I2C:

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>

#include <wiringx.h>

#define I2C_DEV "/dev/i2c-1"

#define I2C_ADDR 0x04

int main(void)
{
int fd_i2c;
int data = 0;

// Duo: milkv_duo
// Duo256M: milkv_duo256m
// DuoS: milkv_duos
if(wiringXSetup("milkv_duo", NULL) == -1) {
wiringXGC();
return -1;
}

if ((fd_i2c = wiringXI2CSetup(I2C_DEV, I2C_ADDR)) <0) {
printf("I2C Setup failed: %d\n", fd_i2c);
wiringXGC();
return -1;
}

// TODO
}

Exemplo de uso de SPI

Aqui está um exemplo de comunicação SPI:

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>

#include <wiringx.h>

int main(void)
{
int fd_spi;

// Duo: milkv_duo
// Duo256M: milkv_duo256m
// DuoS: milkv_duos
if(wiringXSetup("milkv_duo", NULL) == -1) {
wiringXGC();
return -1;
}

if ((fd_spi = wiringXSPISetup(0, 500000)) <0) {
printf("SPI Setup failed: %d\n", fd_spi);
wiringXGC();
return -1;
}

// TODO
}

Exemplo de uso de UART

Aqui está um exemplo de comunicação UART usando UART4 nos pinos 4/5:

#include <stdio.h>
#include <unistd.h>

#include <wiringx.h>

int main() {
struct wiringXSerial_t wiringXSerial = {115200, 8, 'n', 1, 'n'};
char buf[1024];
int str_len = 0;
int i;
int fd;

// Duo: milkv_duo
// Duo256M: milkv_duo256m
// DuoS: milkv_duos
if(wiringXSetup("milkv_duo", NULL) == -1) {
wiringXGC();
return -1;
}

if ((fd = wiringXSerialOpen("/dev/ttyS4", wiringXSerial)) < 0) {
printf("Open serial device failed: %d\n", fd);
wiringXGC();
return -1;
}

wiringXSerialPuts(fd, "Duo Serial Test\n");

while(1)
{
str_len = wiringXSerialDataAvail(fd);
if (str_len > 0) {
i = 0;
while (str_len--)
{
buf[i++] = wiringXSerialGetChar(fd);
}
printf("Duo UART receive: %s\n", buf);
}
}

wiringXSerialClose(fd);

return 0;
}

Método de teste:

O RX do cabo conversor USB para serial está conectado ao pino 4 (UART4_TX) do Duo, o TX do cabo serial está conectado ao pino 5 (UART4_RX) do Duo, e o GND do cabo serial está conectado ao GND do Duo. No computador, configure a porta COM e os parâmetros usando uma ferramenta de depuração serial.

O executável compilado do programa acima é denominado uart_test. Depois de fazer o upload para o Duo via SSH e executá-lo, você poderá ver a string Duo Serial Test recebida na ferramenta serial do seu computador. Se você enviar a string Hello World da ferramenta serial, também verá a string correspondente recebida no terminal do Duo. Isto confirma que a comunicação serial está funcionando corretamente.

duo

2. Configuração do Ambiente de Desenvolvimento

Você também pode usar Ubuntu instalado em uma máquina virtual, Ubuntu instalado via WSL no Windows ou sistemas baseados em Ubuntu usando Docker.

  • Instale as ferramentas que compilam dependências

    sudo apt-get install wget git make
  • Obtenha exemplo de código-fonte

    git clone https://github.com/milkv-duo/duo-examples.git
  • Preparar ambiente de compilação

    cd duo-examples
    source envsetup.sh

    Na primeira vez que você o adquirir, o pacote SDK necessário será baixado automaticamente, com aproximadamente 180 MB de tamanho. Depois de baixado, ele será extraído automaticamente para o diretório duo-examples com o nome duo-sdk. Na próxima vez, se o diretório já existir, ele não será baixado novamente.

  • Teste de compilação

    Tomemos hello-world como exemplo, entre no diretório hello-world e execute make:

    cd hello-world
    make

    Após a compilação ser bem-sucedida, envie o programa executável gerado helloworld  para o dispositivo Duo através da porta de rede ou da rede RNDIS. Por exemplo, o método RNDIS suportado pelo firmware padrão , o IP do Duo é 192.168.42.1, o nome de usuário é root e a senha é milkv.

    scp helloworld root@192.168.42.1:/root/

    Após o envio com sucesso, execute ./helloworld no terminal logado via ssh ou porta serial, e ele irá imprimir Hello, World!

    [root@milkv]~# ./helloworld
    Hello, World!

    Neste ponto, nosso ambiente de compilação e desenvolvimento está pronto para uso.

Como criar seu próprio projeto

Você pode simplesmente copiar os exemplos existentes e fazer as modificações necessárias. Por exemplo, se você precisar manipular um GPIO, você pode consultar o exemplo blink. A intermitência do LED é obtida controlando o nível de tensão do GPIO. O SDK atual utiliza a biblioteca WiringX para operações GPIO, que foi adaptada especificamente para Duo. Você pode encontrar a inicialização da plataforma e os métodos de controle GPIO no código blink.c para referência.

  • Crie seu próprio diretório de projeto chamado my-project.
  • Copie os arquivos blink.c Makefile do exemplo blink para o diretório my-project.
  • Renomeie blink.c com o nome desejado, como gpio_test.c.
  • Modifique Makefile alterando TARGET=blink para TARGET=gpio_test.
  • Modifique gpio_test.c para implementar sua própria lógica de código.
  • Execute o comando  make para compilar.
  • Envie o programa executável gerado gpio_test ao Duo para execução.

Observação:

  • A criação de um novo diretório de projeto não é obrigatória para ser colocado dentro do diretório duo-examples. Você pode escolher qualquer local com base em sua preferência. Antes de executar o comando de compilação  make, basta carregar o ambiente de compilação do diretório duo-examples (source /PATH/TO/duo-examples/envsetup.sh).
  • Dentro do terminal onde o ambiente de compilação (envsetup.sh) é carregado, evite compilar projetos Makefile para outras plataformas como ARM ou X86. Se precisar compilar projetos para outras plataformas, abra um novo terminal.

3. Explicação de cada exemplo

Hello-world

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/hello-world

Um exemplo simples que não interage com os periféricos Duo, apenas imprime a saída “Hello, World!” para verificar o ambiente de desenvolvimento.

Blink

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/blink

Este exemplo demonstra como controlar um LED conectado a um pino GPIO. Ele usa a biblioteca WiringX para alternar o nível de tensão do pino GPIO, fazendo com que o LED pisque.

O código blink.c inclui inicialização da plataforma e métodos de manipulação GPIO da biblioteca WiringX.

Para testar o exemplo blink, que envolve LED piscando, é necessário desabilitar o script responsável pelo piscar automático do LED no firmware padrão do Duo. No terminal Duo, execute o seguinte comando:

mv /mnt/system/blink.sh /mnt/system/blink.sh_backup && sync

Este comando renomeia o script de LED piscando. Após reiniciar o Duo, o LED não piscará mais.

Depois de terminar de testar o programa blink implementado em C, se quiser restaurar o script de piscar do LED, você pode renomeá-lo usando o seguinte comando e reiniciar o Duo:

mv /mnt/system/blink.sh_backup /mnt/system/blink.sh && sync

ADC

adcRead lê a tensão elétrica

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/adc

A leitura do valor medido do ADC é dividida em duas versões: shell script e linguagem C. Após iniciar, selecione o ADC a ser lido de acordo com os prompts de saída. Após a seleção, o valor da tensão medido pelo ADC será impresso em loop.

I2C

Diretório de código-fonte I2C: https://github.com/milkv-duo/duo-examples/tree/main/i2c

Sensor de barômetro e de temperatura BMP280

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/i2c/bmp280_i2c

Este código de exemplo mostra como fazer a interface do Milk-V Duo com o popular sensor de temperatura e pressão de ar BMP280 fabricado pela Bosch.

Sensor de distância de tempo de voo VL53l0X

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/i2c/vl53l0x_i2c

Use o módulo sensor de distância TOF VL53L0X através da interface I2C para ler a distância medida.

OLEDS SSD1306

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/i2c/ssd1306_i2c

Exibir strings no display OLED SSD1306 via interface I2C.

ADXL345 - Sensor de aceleração de 3 eixos


Lê dados de aceleração obtido pelo ADXL345 através da interface I2C , lê a cada 1 segundo e imprime na tela.

Pelo I2C, leia a cada 1 segundo e imprima o resultado na tela.

Módulo de exibição LCM1602

Código fonte: https://github.com/milkv-duo/duo-examples/blob/main/i2c/lcm1602_i2c

Exiba strings na tela LCD 1602 por meio da interface I2C.

Módulo de exibição LCM2004

Código fonte: https://github.com/milkv-duo/duo-examples/blob/main/i2c/lcm2004_i2c

String de exibição na tela LCD de 2004 via interface I2C.

Sensor de cores TCS34725

Código fonte: https://github.com/milkv-duo/duo-examples/blob/main/i2c/tcs34725_i2c

Leia o sensor de cores TCS34725 através da interface I2C e produza os dados obtidos.

SPI

Diretório de código-fonte SPI: https://github.com/milkv-duo/duo-examples/tree/main/spi

Sensor de temperatura MAX6675

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/spi/max6675_spi

Conecte o módulo de medição de termopar tipo K MAX6675 através da interface SPI para medir a temperatura atual no sensor.

Módulo de identificação por radiofrequência RC522

Código fonte: https://github.com/milkv-duo/duo-examples/tree/main/spi/rc522_spi

Conecte o módulo de leitura e gravação RFID RC522 por meio da interface SPI para ler o ID e tipo do cartão e então exibi-lo na tela.

4. Compilando WiringX

O firmware Duo já contém a biblioteca wiringX compilada (/usr/lib/libwiringx.so) e pode ser usado diretamente. Se você precisar compilar o código-fonte do wiringX para gerar a biblioteca, poderá compilá-lo da seguinte maneira.

Nós o compilamos em um host Ubuntu ou outra distribuição Linux ou WSL.

*Observação: parte do código wiringX do Duo ainda não foi integrada ao repositório wiringX upstream. No uso real, dê prioridade ao uso da biblioteca wiringX no firmware Duo.

Baixe

git clone https://github.com/wiringX/wiringX.git

Modifique

Digite o diretório do código:

cd wiringX

O projeto wiringX é compilado usando cmake. Você precisa modificar CMakeLists.txt por meio do editor  vi ou outros para adicionar o conjunto de ferramentas de compilação cruzada e os parâmetros de compilação:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8909393..6918181 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,11 @@ set(CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=/usr/local/lib/,-rpath=/usr/lib/,-rpath=
set(CMAKE_SHARED_LINKER_FLAGS " -Wl,-rpath=/usr/local/lib/,-rpath=/usr/lib/,-rpath=/lib/")
set(CMAKE_MODULE_LINKER_FLAGS " -Wl,-rpath=/usr/local/lib/,-rpath=/usr/lib/,-rpath=/lib/")

+set(CMAKE_C_COMPILER "${CMAKE_CURRENT_SOURCE_DIR}/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc")
+set(CMAKE_CXX_COMPILER "${CMAKE_CURRENT_SOURCE_DIR}/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-g++")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64")
+
# Start uninstaller generator
function(WRITE_UNINSTALL_TARGET_SCRIPT)
# Create uninstall target template file, if it doesn't exist...

Existem duas variáveis ​​que precisam ser anotadas e configuradas de acordo com o seu próprio caminho de arquivo:

  • CMAKE_C_COMPILER : O caminho para o gcc no conjunto de ferramentas de compilação cruzada
  • CMAKE_CXX_COMPILER : caminho para g++ na cadeia de ferramentas de compilação cruzada

Link para download do conjunto de ferramentas de compilação cruzada: host-tools.tar.gz . Você pode baixá-lo e descompactá-lo através do comando wget:

wget https://sophon-file.sophon.cn/sophon-prod-s3/drive/23/03/07/16/host-tools.tar.gz
tar -xf host-tools.tar.gz

Se você já compilou duo-buildroot-sdk , o diretório  host-tools em seu diretório raiz é o diretório da cadeia de ferramentas cruzada. Não há necessidade de fazer download novamente, você pode modificar diretamente o campo CMAKE_CURRENT_SOURCE_DIR para especificar o diretório. Ou crie um link virtual apontando para o diretório.

Modifique o código

Como as definições relacionadas ao tempo no conjunto de ferramentas cruzadas são ligeiramente diferentes daquelas no WiringX, adicione as duas linhas de modificação a seguir:

diff --git a/src/wiringx.c b/src/wiringx.c
index 034674a..4171a75 100644
--- a/src/wiringx.c
+++ b/src/wiringx.c
@@ -113,6 +113,9 @@ static struct spi_t spi[2] = {
} while(0)
#endif

+typedef time_t __time_t;
+typedef suseconds_t __suseconds_t;
+
/* Both the delayMicroseconds and the delayMicrosecondsHard
are taken from wiringPi */
static void delayMicrosecondsHard(unsigned int howLong) {

A compilação no cmake criará alguns diretórios e arquivos intermediários, então criamos um novo diretório de construção e entramos neste diretório para concluir a compilação:

mkdir build
cd build
cmake ..
make

Após a conclusão da compilação, o libwiringx.so gerado no diretório  build atual é a biblioteca wiringX que precisamos.

Pontos a considerar

Se você encontrar erros de compilação, você pode tentar alterar a versão do cmake. Por exemplo, você pode instalar manualmente a versão  3.27.6 mais recente:

wget https://github.com/Kitware/CMake/releases/download/v3.27.6/cmake-3.27.6-linux-x86_64.sh
chmod +x cmake-3.27.6-linux-x86_64.sh
sudo sh cmake-3.27.6-linux-x86_64.sh --skip-license --prefix=/usr/local/

cmake instalado manualmente está no formato /usr/local/bin. Neste momento, utilize o comando cmake --version para verificar seu número de versão, que deve ser:

cmake version 3.27.6

wiringX APIs

Geral

int wiringXSetup(char *name, ...)
int wiringXValidGPIO(int pin)
void delayMicroseconds(unsigned int ms)
int wiringXGC(void)
char *wiringXPlatform(void)

GPIO

int pinMode(int pin, pinmode_t mode)
int digitalRead(int pin)
int digitalWrite(int pin, enum digital_value_t value)
int waitForInterrupt(int pin, int ms)
int wiringXISR(int pin, enum isr_mode_t mode)

I2C

int wiringXI2CSetup(const char *dev, int addr)
int wiringXI2CRead(int fd)
int wiringXI2CReadReg8(int fd, int reg)
int wiringXI2CReadReg16(int fd, int reg)
int wiringXI2CWrite(int fd, int reg)
int wiringXI2CWriteReg8(int fd, int reg, int value8)
int wiringXI2CWriteReg16(int fd, int reg, int value16)

SPI

int wiringXSPISetup(int channel, int speed)
int wiringXSPIDataRW(int channel, unsigned char *data, int len)
int wiringXSPIGetFd(int channel)

UART

int wiringXSerialOpen(const char *dev, struct wiringXSerial_t serial)
void wiringXSerialClose(int fd)
void wiringXSerialFlush(int fd)
void wiringXSerialPutChar(int fd, unsigned char c)
void wiringXSerialPuts(int fd, const char *s)
void wiringXSerialPrintf(int fd, const char *message, ...)
int wiringXSerialDataAvail(int fd)
int wiringXSerialGetChar(int fd)

  • PWM

    • Número do pino PWM do Duo
    PWMPIN NAMEPin#Pin#PIN NAME
    GP0
    1
    40
    VBUS
    GP1
    2
    39
    VSYS
    GND
    3
    38
    GND
    10GP2
    4
    37
    3V3_EN
    11GP3
    5
    36
    3V3(OUT)
    5GP4
    6
    35
    6GP5
    7
    34
    GND
    8
    33
    GND
    9GP6
    9
    32
    GP27
    8GP7
    10
    31
    GP26
    7GP8
    11
    30
    RUN
    4GP9
    12
    29
    GP22
    GND
    13
    28
    GND
    GP10
    14
    27
    GP21
    GP11
    15
    26
    GP20
    4GP12
    16
    25
    GP19
    5GP13
    17
    24
    GP18
    GND
    18
    23
    GND
    GP14
    19
    22
    GP17
    GP15
    20
    21
    GP16

    PWM

    wiringXPWMSetPeriod(int pin, long period)
    int wiringXPWMSetDuty(int pin, long duty_cycle)
    int wiringXPWMSetPolarity(int pin, int polarity)
    int wiringXPWMEnable(int pin, int enable)





  • Questões: suporte@smartcore.com.br

    Referências


    Sobre a SMARTCORE

    A SMARTCORE FORNECE CHIPS E MÓDULOS PARA IOT, COMUNICAÇÃO WIRELESS, BIOMETRIA, CONECTIVIDADE, RASTREAMENTO E AUTOMAÇÃO. NOSSO PORTFÓLIO INCLUI MODEM 2G/3G/4G/NB-IOT, SATELITAL, MÓDULOS WIFI, BLUETOOTH, GPS, SIGFOX, LORA, LEITOR DE CARTÃO, LEITOR QR CCODE, MECANISMO DE IMPRESSÃO, MINI-BOARD PC, ANTENA, PIGTAIL, BATERIA, REPETIDOR GPS E SENSORES.

    Nenhum comentário:

    Postar um comentário