Mraa 使用
MRAA 简介
Eclipse Mraa(Libmraa)是一个包含 Java、Python 和 JavaScript 绑定的 C/C++ 库,用于连接各种物联网和边缘平台上的 I/O 引脚和总线,其 API 结构合理,端口名称/编号与所在的电路板相匹配。
安装 MRAA
- 卸载系统原装的包:
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 Add_Radxa_ROCK5C_Support origin/Add_Radxa_ROCK5C_Support 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 上
mraa-uart
使用例子示例代码
GPIO
- C
- Python
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 会有一个闪烁的效果。
blink.py
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import mraa
import time
pin = 3 # set pin num
try:
gpio = mraa.Gpio(pin)
gpio.dir(mraa.DIR_OUT) # set mode as input
while True:
gpio.write(1)
time.sleep(1)
gpio.write(0)
time.sleep(1)
except KeyboardInterrupt:
print("\nstop\n")
使用方法:
-
将 led 信号脚连接到 PIN3,VCC 接板子 VCC,GND 接板子 GND
-
测试
sudo python3 Blink.py
若测试成功,led 会有一个闪烁的效果。
I2C
- C
- Python
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 会有一个流水灯效果。
led_i2c_blink.py
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
import mraa
import time
# initialise I2C
x = mraa.I2c(2)
x.address(0x20)
while True:
x.writeReg(0x11, 0x01)
time.sleep(0.5)
x.writeReg(0x12, 0x02)
time.sleep(0.5)
x.writeReg(0x13, 0x03)
time.sleep(0.5)
x.writeReg(0x14, 0x04)
time.sleep(0.5)
x.writeReg(0x15, 0x05)
time.sleep(0.5)
x.writeReg(0x16, 0x06)
使用方法:
-
打开 I2C2 的 Overlay,并重启
-
接线: SDA <--> SDA, SCL <--> SCL, GDN <--> GND, VCC <--> VCC
-
打开终端,输入以下命令进行测试
sudo python3 led_i2c_blink.py
PWM
由于 pwmchip 驱动的改动,MRAA 的 PWM 功能在 6.1 内核上无法正常工作
- C
- Python
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 会有一个呼吸灯的效果。
led_pwm_fade_out.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import mraa
import time
class Led_pwm_fade_out:
def __init__(self, pwm_pin):
self.pwm_pin = pwm_pin
self.pwm = mraa.Pwm(pwm_pin)
self.duty_cycle = 0.0
self.pwm_freq = 1e3
self.pwm_step = 0.05
def initialize(self):
if self.pwm == None:
exit()
if self.pwm.period_us(self.pwm_freq) != mraa.SUCCESS:
mraa.Pwm.close()
exit()
if self.pwm.enable(True) != mraa.SUCCESS:
mraa.Pwm.close()
exit()
def set_duty_cycle(self, duty_cycle):
self.duty_cycle = duty_cycle
self.pwm.write(duty_cycle)
def run(self):
try:
while True:
self.set_duty_cycle(self.duty_cycle)
self.duty_cycle += self.pwm_step
if self.duty_cycle > 1.0:
self.duty_cycle = 0.0
print("0x%x\n" %self.pwm.read(self.pwm))
time.sleep(1)
except:
print("Error")
finally:
self.pwm.write(0) # Stop PWM
self.pwm.disable() # Disable PWM
self.pwm.close() # Close PWM
if __name__ == '__main__':
pwm_controller = Led_pwm_fade_out(28)
pwm_controller.initialize()
pwm_controller.run()
使用方法:
-
打开某个 PIN 的 PWM Overlay,并重启,这里以 PIN28 的 PWM 为例
-
将 led 的信号引脚接 PIN28 引脚,VCC 接 板子 VCC, GDN 接 板子 GND
-
测试
sudo python3 Led_pwm_fade_out.py
若测试成功,led 会有一个呼吸灯的效果。
SPI
- C
- Python
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"
Spi_test.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import mraa
import time
class Spi_test:
def __init__(self, spi_index):
self.spidev = mraa.Spi(spi_index)
self.freq = 400000
def initialize(self):
if self.spidev.frequency(self.freq) != mraa.SUCCESS:
exit()
if self.spidev.lsbmode(False) != mraa.SUCCESS:
exit()
def write_byte(self, value):
return self.spidev.writeByte(value)
def close(self):
self.close()
if __name__ == '__main__':
# create spi device
spi_device = Spi_test(0)
# init
spi_device.initialize()
try:
while True:
# value = ...
print("0x%x\n" % spi_device.write_byte(0xaa))
time.sleep(1)
except:
print("Error")
finally:
spi_device.close()
使用方法:
-
打开 spi0 即 spidev0.0 的 Overlay,并重启
-
测试
打开终端,分别输入以下命令进行测试
sudo python3 Spi_test.py
若测试成功,终端会输出 "0xaa"
UART
- C
- Python
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"
Snd.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import mraa
import time
class Snd:
def __init__(self, uart_id, baurd_rate, parity, stopbits):
self.uart_id = uart_id
self.uart_snd = mraa.Uart(uart_id)
self.uart_snd.setBaudRate(baurd_rate)
self.uart_snd.setMode(parity, mraa.UART_PARITY_NONE, stopbits)
self.uart_snd.setFlowcontrol(False, False)
def send_message(self, message):
snd_msg = bytearray(message, "ascii")
self.uart_snd.write(snd_msg)
self.uart_snd.flush()
if __name__ == '__main__':
# init uart
uart_snd = Snd(1, 115200, 8, 1)
while True:
uart_snd.send_message("Hello, this is snd side.\n")
time.sleep(1)
Recv.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import mraa
import time
import sys
class Recv:
def __init__(self, uart_id, baurd_rate, parity, stopbits):
self.uart_id = uart_id
self.baurd_rate = baurd_rate
self.parity = parity
self.stopbits = stopbits
self.uart = mraa.Uart(uart_id)
self.initialize_uart()
def initialize_uart(self):
self.uart.setBaudRate(self.baurd_rate)
self.uart.setMode(self.parity, mraa.UART_PARITY_NONE, self.stopbits)
self.uart.setFlowcontrol(False, False)
def run(self):
try:
while True:
if self.uart.dataAvailable():
data_byte = self.uart.readStr(125)
print(data_byte)
# Just a two-way half-duplex communication example, "X" is a flag
if data_byte == "X":
self.uart.writeStr("Yes, master!")
except KeyboardInterrupt:
print("Exiting...")
self.uart.writeStr("Exiting...")
self.uart.close()
sys.exit()
if __name__ == '__main__':
uart_receiver = Recv(2, 115200, 8, 1)
uart_receiver.run()
使用方法:
-
打开两个 UART 的 Overlay, 这里以 uart4 和 uart6 即 /dev/ttyS4 和 /dev/ttyS6 为例
-
连个 UART 的 TX RX 引脚交叉接线
-
测试
打开两个终端,分别输入以下命令进行测试
sudo python3 Recv.py
sudo python3 Snd.py
若测试成功, uart6 会接收到来自 uart4 发送的循环消息 "Hello, this is snd side."