跳到主要内容

Mraa 使用

MRAA 简介

Eclipse Mraa(Libmraa)是一个包含 Java、Python 和 JavaScript 绑定的 C/C++ 库,用于连接各种物联网和边缘平台上的 I/O 引脚和总线,其 API 结构合理,端口名称/编号与所在的电路板相匹配。

安装 MRAA

sudo apt-get update -y
sudo apt-get install libmraa2 libmraa-dev libmraa-java python3-mraa mraa-tools -y
  • 卸载系统原装的包:
cd /
sudo apt purge *mraa*
  • 源码安装

sudo apt-get update -y
sudo apt-get install git cmake build-essential swig4.0 python-dev python3-dev libnode-dev  cmake libjson-c-dev libgtest-dev pkg-config cmake-data -y
git clone https://github.com/nascs/mraa.git
cd mraa
git checkout -b undefined origin/undefined
mkdir build && cd build
cmake .. && make -j${nproc} && sudo make install && sudo ldconfig

MRAA 命令行工具

GPIO

  • mraa-gpio list: 列出所有可用引脚
  • mraa-gpio get pin: 获取引脚状态
  • mraa-gpio set pin level: 设置引脚状态
  • mraa-gpio version: 获取版本信息

I2C

  • mraa-i2c list: 列出所有可用 I2C 总线
  • mraa-i2c version: 获取 mraa 版本和板卡名称
  • mraa-i2c detect bus: 列出指定总线上检测到的设备
  • mraa-i2c get bus device reg: 从指定设备寄存器中获取值
  • mraa-i2c set bus device reg value: 将指定设备寄存器设置为值

UART

  • mraa-uart list: 列出所有可用 UART 端口
  • mraa-uart dev dev_num baud customized_baud send str: 将字符串 str 发送到指定端口
  • mraa-uart dev dev_num baud customized_baud recv 1000: 读取端口 dev_num 上的数据,最多读取 1000 字节,并将它们显示在 stdout 上

asciicast

mraa-uart 使用例子

示例代码

GPIO

blink.c

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "mraa/gpio.h"

/* gpio declaration */
#define GPIO_PIN 3

volatile sig_atomic_t flag = 1;

void sig_handler(int signum) {
if(signum == SIGINT) {
fprintf(stdout, "Exiting...\n");
flag = 0;
}
}

int main(void) {
mraa_result_t status = MRAA_SUCCESS;
mraa_gpio_context gpio;

signal(SIGINT, sig_handler);

mraa_init();

/* initialize GPIO */
gpio = mraa_gpio_init(GPIO_PIN);
if(gpio == NULL) {
fprintf(stderr, "Failed to initialize GPIO %d\n", GPIO_PIN);
mraa_deinit();
return EXIT_FAILURE;
}

/* set output */
status = mraa_gpio_dir(gpio, MRAA_GPIO_OUT);
if(status != MRAA_SUCCESS) {
goto err_exit;
}

while(flag) {
status = mraa_gpio_write(gpio, 1);
if(status != MRAA_SUCCESS) {
goto err_exit;
}
sleep(1);

status = mraa_gpio_write(gpio, 0);
if(status != MRAA_SUCCESS) {
goto err_exit;
}

sleep(1);
}

/* release gpio's */
status = mraa_gpio_close(gpio);
if(status != MRAA_SUCCESS) {
goto err_exit;
}

mraa_deinit();

return EXIT_SUCCESS;

err_exit:
mraa_result_print(status);

mraa_deinit();

return EXIT_FAILURE;
}

使用方法:

  • 将 led 信号脚连接到 PIN3,VCC 接板子 VCC,GND 接板子 GND

  • 测试

gcc blink.c -lmraa -o blink && sudo ./blink

若测试成功,led 会有一个闪烁的效果。

I2C

led_i2c_blink.c

#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "mraa/i2c.h"

#define I2C_BUS 2
#define LED_ADDRESS 0x20

volatile sig_atomic_t flag = 1;

void sig_handler(int signum) {
if (signum == SIGINT) {
fprintf(stdout, "Exiting...\n");
flag = 0;
}
}

int main() {
mraa_result_t status = MRAA_SUCCESS;
mraa_i2c_context i2c;

signal(SIGINT, sig_handler);
mraa_init();

i2c = mraa_i2c_init(I2C_BUS);
if(i2c == NULL) {
fprintf(stderr, "Failed to initialize I2C\n");
mraa_deinit();
return EXIT_FAILURE;
}

/* set slave address */
status = mraa_i2c_address(i2c, LED_ADDRESS);
if(status != MRAA_SUCCESS) {
return -1;
}

while(1) {
/**
* Write a single word to an i2c context
*
* @param dev The i2c context
* @param data The word to write
* @param command The register
* @return Result of operation
*/
mraa_i2c_write_byte_data(i2c, 0x23, 0x08);
sleep(1);
mraa_i2c_write_byte_data(i2c, 0x21, 0x01);
sleep(1);
mraa_i2c_write_byte_data(i2c, 0x33, 0x12);
sleep(1);
mraa_i2c_write_byte_data(i2c, 0x1f, 0x42);
sleep(1);
}

return 0;
}

使用方法:

  • 打开 I2C2 的 Overlay,并重启

  • 接线: SDA <--> SDA, SCL <--> SCL, GDN <--> GND, VCC <--> VCC

  • 打开终端,输入以下命令进行测试

gcc led_i2c_blink.c -lmraa -o led_i2c_blink && sudo ./led_i2c_blink

若测试成功, I2C Led 会有一个流水灯效果。

PWM

注意

由于 pwmchip 驱动的改动,MRAA 的 PWM 功能在 6.1 内核上无法正常工作

led_pwm_fade_out.c

#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "mraa/pwm.h"

/* PWM period in us */
#define PWM_FREQ 1e3

char *usage =
"Usage: %s pwm_pin\n"
"Example: %s 3\n";

int main(int argc, char *argv[]) {
char usagestr[130];
char *str = NULL;
int invalid = 0, pwm_num = 0;
mraa_result_t status = MRAA_SUCCESS;
mraa_pwm_context pwm;
float output = 0.0;
float duty_cycle = 0.0;
float step = 0.05;

memset(usagestr, '\0', 130);
// expect only 1 argument => argc must be 2
if(argc != 2) {
snprintf(usagestr, 129, usage, argv[0], argv[0]);
puts(usagestr);
return -1;
}
// check for a valid, numeric argument
str = argv[1];
while(*str != '\0') {
if(!isdigit(*str)) {
invalid = 1;
}
str++;
}
if(invalid == 1) {
printf("%s: Invalid GPIO %s\n", argv[0], argv[2]);
return -1;
}
pwm_num = atoi(argv[1]);

mraa_init();

/* init pwm */
if((pwm = mraa_pwm_init(pwm_num)) == NULL) {
printf("Failed to initialize PWM\n");
mraa_deinit();
return -1;
}
/* set period */
if((status = mraa_pwm_period_us(pwm, PWM_FREQ))) {
printf("Failed to set pwm period\n");
return -1;
}
/* enable pwm */
if((status = mraa_pwm_enable(pwm, 1)) != MRAA_SUCCESS) {
printf("Failed to Enable PWM\n");
return -1;
}

while (1) {
// Set pwm duty cycle
mraa_pwm_write(pwm, duty_cycle);

// sleep 100 ms
usleep(100000);

// change the duty cycle
duty_cycle += step;
if (duty_cycle > 1.0) {
duty_cycle = 0.0;
}

printf("PWM value: %f\n", mraa_pwm_read(pwm));
}

mraa_pwm_close(pwm);

return 0;
}

使用方法:

  • 打开某个 PIN 的 PWM Overlay,并重启,这里以 PIN28 的 PWM 为例

  • 将 led 的信号引脚接 PIN28 引脚,VCC 接 板子 VCC, GDN 接 板子 GND

  • 测试

gcc led_pwm_fade_out.c -lmraa -o led_pwm_fade_out && sudo ./led_pwm_fade_out 28

若测试成功,led 会有一个呼吸灯的效果。

SPI

spi_test.c

#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

/* mraa header */
#include "mraa/spi.h"

/* SPI declaration */
#define SPI_BUS 0

/* SPI frequency in Hz */
#define SPI_FREQ 400000

int main(int argc, char** argv) {
mraa_result_t status = MRAA_SUCCESS;
mraa_spi_context spi;
int i, j;

/* initialize mraa for the platform (not needed most of the times) */
mraa_init();

//! [Interesting]
/* initialize SPI bus */
spi = mraa_spi_init(SPI_BUS);
if (spi == NULL) {
fprintf(stderr, "Failed to initialize SPI\n");
mraa_deinit();
return EXIT_FAILURE;
}

/* set SPI frequency */
status = mraa_spi_frequency(spi, SPI_FREQ);
if (status != MRAA_SUCCESS)
goto err_exit;
/* set big endian mode */
status = mraa_spi_lsbmode(spi, 0);
if (status != MRAA_SUCCESS) {
goto err_exit;
}

while(1) {
printf("0x%x\n",mraa_spi_write(spi, 0xaa));
sleep(1);
}
err_exit:
mraa_result_print(status);

/* stop spi */
mraa_spi_stop(spi);

/* deinitialize mraa for the platform (not needed most of the times) */
mraa_deinit();

return EXIT_FAILURE;
}

使用方法:

  • 打开 spi0 即 spidev0.0 的 Overlay,并重启

  • 测试

打开终端,分别输入以下命令进行测试

gcc spi_test.c -o spi_test && sudo ./spi_test

若测试成功,终端会输出 "0xaa"

UART

snd.c

#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "mraa/uart.h"

#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif

char *usage =
"Usage: %s uart_path_id msg\n"
"Example: %s 4 'this is test message'\n";

int main(int argc, char *argv[]) {
char usagestr[130];
char *str = NULL;
int invalid = 0;
mraa_uart_context snd_uart;
mraa_result_t status = MRAA_SUCCESS;

memset(usagestr, '\0', 130);
if(argc != 3) {
snprintf(usagestr, 129, usage, argv[0], argv[0]);
puts(usagestr);
return -1;
}

mraa_init();

char uart_path[15];
snprintf(uart_path, sizeof(uart_path), "/dev/ttyS%d", atoi(argv[1]));
printf("uart_path: %s, msg: %s\n", uart_path, argv[2]);

if((snd_uart = mraa_uart_init_raw(uart_path)) == NULL) {
printf("Failed to initialize UART\n");
mraa_deinit();
return -1;
}

if ((status = mraa_uart_set_baudrate(snd_uart, 115200)) != MRAA_SUCCESS) {
return -1;
}

if ((status = mraa_uart_set_mode(snd_uart, 8, MRAA_UART_PARITY_NONE, 1)) != MRAA_SUCCESS) {
return -1;
}

if ((status = mraa_uart_set_flowcontrol(snd_uart, FALSE, FALSE)) != MRAA_SUCCESS) {
return -1;
}

while(1) {
mraa_uart_write(snd_uart, argv[2], strlen(argv[2]));
sleep(1);
}

mraa_uart_stop(snd_uart);
mraa_deinit();

return 0;
}

recv.c

#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "mraa/uart.h"

#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif

char *usage =
"Usage: %s uart_path_id\n"
"Example: %s 4\n";


int main(int argc, char *argv[]) {
char usagestr[130];
char *str = NULL;
int invalid = 0, uart_id = 0;
mraa_uart_context recv_uart;
mraa_result_t status = MRAA_SUCCESS;

memset(usagestr, '\0', 130);
if(argc != 2) {
snprintf(usagestr, 129, usage, argv[0], argv[0]);
puts(usagestr);
return -1;
}
// check for a valid, numeric argument
str = argv[1];
while(*str != '\0') {
if(!isdigit(*str)) {
invalid = 1;
}
str++;
}
if(invalid == 1) {
printf("%s: Invalid GPIO %s\n", argv[0], argv[1]);
return -1;
}

mraa_init();

char uart_path[15];
snprintf(uart_path, sizeof(uart_path), "/dev/ttyS%d", atoi(argv[1]));
printf("uart_path: %s\n", uart_path);

if((recv_uart = mraa_uart_init_raw(uart_path)) == NULL) {
printf("Failed to initialize UART\n");
mraa_deinit();
return -1;
}

if ((status = mraa_uart_set_baudrate(recv_uart, 115200)) != MRAA_SUCCESS) {
return -1;
}

if ((status = mraa_uart_set_mode(recv_uart, 8, MRAA_UART_PARITY_NONE, 1)) != MRAA_SUCCESS) {
return -1;
}

if ((status = mraa_uart_set_flowcontrol(recv_uart, FALSE, FALSE)) != MRAA_SUCCESS) {
return -1;
}

char recv_buff[1024];
while(1) {
if (mraa_uart_data_available(recv_uart, 1) == TRUE) {
mraa_uart_read(recv_uart, recv_buff, sizeof(recv_buff));
printf("recv: %s\n", recv_buff);
sleep(1);
}
}
}

使用方法:

  • 打开两个 UART 的 Overlay, 这里以 uart4 和 uart6 即 /dev/ttyS4 和 /dev/ttyS6 为例

  • 连个 UART 的 TX RX 引脚交叉接线

  • 测试

打开两个终端,分别输入以下命令进行测试

gcc recv.c -lmraa -o recv && sudo ./recv 6 # /dev/ttyS6
gcc snd.c -lmraa -o  snd && sudo ./snd 4 "hello, this is test"  # /dev/ttyS4

若测试成功, uart6 会接收到来自 uart4 发送的循环消息 "hello, this is test"

参考