android-studio-2022.1.1.19-windows.exe
运行安装一、 创建工程,如下图动图所示。
二、添加系统签名文件
YY3568.keystore
存放到 security 文件夹下。File
-> Project Structure...
-> Modules
-> Signing Configs
Signing Configs
下方的 +
,输入 YY3568
, 回车。在相应的位置填入的信息。Store File
:点击文件夹图标选择 YY3568.keystore 存放的路径Store Password
:填入 youyeetooKey Alias
:填入 youyeetooKey Password
:填入 youyeetooBuild Variants
-> Build Types
-> debug
-> Signing Config
。选择$signingConfigs.YY3568
。动图如下所示。三、添加系统权限
在 AndroidManifest.xml
文件里添加
android:sharedUserId="android.uid.system"
tools:ignore="Deprecated"
四、添加framework.jar
cat YY3568-Android11.tar.gz0* > YY3568-Android11.tar.gz
tar -xzvf YY3568-Android11.tar.gz
cd YY3568-Android11
source build/envsetup.sh
lunch YY3568-userdebug
./build.sh -UKAu
framework.jar
(在Ubuntu主机上进行)make javac-check-framework
打包结束,生成 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
文件。将该 classes.jar 文件复制到工程目录下的 app/libs 目录下。
Add As Library...
将
dependencies {
...
implementation files('libs\\classes.jar')
...
}
修改为
dependencies {
...
compileOnly files('libs\\classes.jar')
...
}
GPIO全称: General-Purpose Input/Output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。
YY3568引脚分类
YY3568有三十个对外开放的GPIO口,如下图所示
YY3568:/ # ls -l /sys/class/gpio/
total 0
--w------- 1 root root 4096 2023-01-16 11:01 export
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip0 -> ../../devices/platform/fdd60000.gpio/gpio/gpiochip0
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip128 -> ../../devices/platform/fe770000.gpio/gpio/gpiochip128
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip32 -> ../../devices/platform/fe740000.gpio/gpio/gpiochip32
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip511 -> ../../devices/platform/fdd40000.i2c/i2c-0/0-0020/rk805-pinctrl/gpi
o/gpiochip511
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip64 -> ../../devices/platform/fe750000.gpio/gpio/gpiochip64
lrwxrwxrwx 1 root root 0 2023-01-16 11:01 gpiochip96 -> ../../devices/platform/fe760000.gpio/gpio/gpiochip96
--w------- 1 root root 4096 2023-01-16 11:01 unexport
每块芯片可以有N组GPIO,每组GPIO最多有32个GPIO口。所以每块芯片最多有N*32个GPIO口。
在我们的YY3568中,GPIO口是如此定义的。
GPIO0_A0~GPIO0_A7 GPIO0_B0~GPIO1_B7 .... GPIO0_D0~GPIO1_D7
GPIO1_A0~GPIO1_A7 .....
GPIO2_A0~GPIO2_A7 .....
GPIO3_A0~GPIO3_A7 .....
GPIO4_A0~GPIO4_A7 .....
由此不难发现规律 A0~D7则代表一组GPIO 一共有4*8=32个
我们可以通过这种规律不难计算出管脚图中的GPIO0_D4对应的就是gpio28
控制接口主要是指上图中的/sys/class/gpio/export和/sys/class/gpio/unexport两个接口,用户态可以通过这两个接口实现对GPIO的控制。
使用实例
//进入adb需要获取root权限
D:\code\ThinkerEdgeR\adb>adb root && adb remount && adb shell
remount succeeded
//进入gpio目录
YY3568:/ # cd /sys/class/gpio
//查看目录下文件
YY3568:/sys/class/gpio # ls
export gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
//将gpio28从内核空间导出回用户空间
YY3568:/sys/class/gpio # echo 28 > export
//发现多出了一个gpio28目录
YY3568:/sys/class/gpio # ls
export gpio28 gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
//将GPIO编号28的取消GPIO控制从内核空间到用户空间的导出
YY3568:/sys/class/gpio # echo 28 > unexport
//再次查看目录,发现gpio28已经被移除
YY3568:/sys/class/gpio # ls
export gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
//备注:如果export不了,说明AP侧(kernel等)被占用,如果值改不了(在后面的代码中体现),说明BP侧(modem)被占用。
GPIO信号,就是指GPIO本身。对应的是/sys/class/gpio/gpioN/,它拥有多个属性。我们通过对这些属性的控制,可以实现对于GPIO口的控制。
YY3568:/sys/class/gpio # cd gpio28
YY3568:/sys/class/gpio/gpio28 # ls
active_low device direction edge power subsystem uevent value
GPIO控制器,用于表示GPIO 控制实现的初始GPIO,其路径为/sys/class/gpio/gpiochipN/。比如/sys/class/gpio/gpiochip0/ 则表示实现GPIO控制器的初始化编号为0。GPIO控制器的属性为只读属性,包括base、label和ngpio等多个。
”base”属性,和gpiochipN的N代表的含义相同,表示被该组GPIO控制器实现的第一个GPIO.
”ngpio”属性,用于表示该控制器支持多少个GPIO,支持的GPIO编号为从N到N+ngpio-1
”label”属性,用于判断控制器,并不总是唯一的
YY3568:/sys/class/gpio/gpiochip0 # ls
base device label ngpio power subsystem uevent
1|YY3568:/sys/class/gpio/gpiochip0 # cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (gpio0-0): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 1 (gpio0-1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 2 (gpio0-2): 0-0020 (GPIO UNCLAIMED) function pmic group soc_slppin_gpio
pin 3 (gpio0-3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 4 (gpio0-4): fe2b0000.dwmmc (GPIO UNCLAIMED) function sdmmc0 group sdmmc0-det
pin 5 (gpio0-5): vcc5v0-otg-regulator gpio0:5 function usb group vcc5v0-otg-en
pin 6 (gpio0-6): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 7 (gpio0-7): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 8 (gpio0-8): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 9 (gpio0-9): fdd40000.i2c (GPIO UNCLAIMED) function i2c0 group i2c0-xfer
pin 10 (gpio0-10): fdd40000.i2c (GPIO UNCLAIMED) function i2c0 group i2c0-xfer
pin 11 (gpio0-11): fe5a0000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 12 (gpio0-12): fe5a0000.i2c (GPIO UNCLAIMED) function i2c1 group i2c1-xfer
pin 13 (gpio0-13): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 14 (gpio0-14): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 15 (gpio0-15): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 16 (gpio0-16): (MUX UNCLAIMED) gpio0:16
pin 17 (gpio0-17): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 18 (gpio0-18): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 19 (gpio0-19): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 20 (gpio0-20): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 21 (gpio0-21): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 22 (gpio0-22): fe6e0030.pwm (GPIO UNCLAIMED) function pwm7 group pwm7-pins
pin 23 (gpio0-23): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 24 (gpio0-24): fiq-debugger (GPIO UNCLAIMED) function uart2 group uart2m0-xfer
pin 25 (gpio0-25): fiq-debugger (GPIO UNCLAIMED) function uart2 group uart2m0-xfer
pin 26 (gpio0-26): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 27 (gpio0-27): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 28 (gpio0-28): (MUX UNCLAIMED) gpio0:28
...
Android系统需要通过adb登录进行操作,并且切换root权限。
使用双母头USB线连接YY3568和电脑,启动adb命令窗口,输入如下命令:
$ adb root
$ adb remount
$ adb shell
YY3568:/ # cd /sys/class/gpio
YY3568:/sys/class/gpio # ls
export gpio28 gpiochip0 gpiochip128 gpiochip32 gpiochip511 gpiochip64 gpiochip96 unexport
YY3568:/sys/class/gpio # echo 28 > export //28代表gpio号
YY3568:/sys/class/gpio # cd gpio28/
YY3568:/sys/class/gpio/gpio28 # cat direction
out
YY3568:/sys/class/gpio/gpio28 # echo in > direction
YY3568:/sys/class/gpio/gpio28 # cat direction //查看gpio口状态
in
YY3568:/sys/class/gpio/gpio28 # cat value
0 // 0 代表读取到低电平 1 代表读取到高电平
YY3568:/sys/class/gpio/gpio28 # echo out > direction
YY3568:/sys/class/gpio/gpio28 # cat direction //查看gpio口状态
out
YY3568:/sys/class/gpio/gpio28 # echo 1 > value //1 代表输出高电平 0 代表输出低电平
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/stat.h>
#define GPIO_PIN 28
typedef enum {
GPIO_OUTPUT = 1,
GPIO_OUTPUT_HIGH,
GPIO_OUTPUT_LOW,
GPIO_INPUT,
} GPIO_DIRECT;
int gpio_direction(int gpio, int dir)
{
int ret = 0;
char buf[128];
sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
int gpiofd = open(buf, O_WRONLY);
if(gpiofd < 0) {
perror("Couldn't open IRQ file");
ret = -1;
}
if(dir == GPIO_OUTPUT && gpiofd){
if (3 != write(gpiofd, "out", 3)) {
perror("Couldn't set GPIO direction to out");
ret = -2;
}
}
else if(dir == GPIO_OUTPUT_HIGH && gpiofd){
if (4 != write(gpiofd, "high", 4)) {
perror("Couldn't set GPIO direction to out high");
ret = -3;
}
}
else if (dir == GPIO_OUTPUT_LOW && gpiofd){
if (3 != write(gpiofd, "low", 3)){
perror("Couldn't set GPIO direction to out low");
ret = -4;
}
}
else if(gpiofd) {
if(2 != write(gpiofd, "in", 2)) {
perror("Couldn't set GPIO directio to in");
ret = -5;
}
}
close(gpiofd);
return ret;
}
int gpio_set_edge(int gpio, int rising, int falling)
{
int ret = 0;
char buf[128];
sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
int gpiofd = open(buf, O_WRONLY);
if(gpiofd < 0) {
perror("Couldn't open IRQ file");
ret = -1;
}
if(gpiofd && rising && falling) {
if(4 != write(gpiofd, "both", 4)) {
perror("Failed to set IRQ to both falling & rising");
ret = -2;
}
} else {
if(rising && gpiofd) {
if(6 != write(gpiofd, "rising", 6)) {
perror("Failed to set IRQ to rising");
ret = -2;
}
} else if(falling && gpiofd) {
if(7 != write(gpiofd, "falling", 7)) {
perror("Failed to set IRQ to falling");
ret = -3;
}
}
}
close(gpiofd);
return ret;
}
int gpio_export(int gpio)
{
int efd;
char buf[50];
int gpiofd, ret;
/* Quick test if it has already been exported */
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
efd = open(buf, O_WRONLY);
if(efd != -1) {
close(efd);
return 0;
}
efd = open("/sys/class/gpio/export", O_WRONLY);
if(efd != -1) {
sprintf(buf, "%d", gpio);
ret = write(efd, buf, strlen(buf));
if(ret < 0) {
perror("Export failed");
return -2;
}
close(efd);
} else {
// If we can't open the export file, we probably
// dont have any gpio permissions
return -1;
}
return 0;
}
void gpio_unexport(int gpio)
{
int gpiofd, ret;
char buf[50];
gpiofd = open("/sys/class/gpio/unexport", O_WRONLY);
sprintf(buf, "%d", gpio);
ret = write(gpiofd, buf, strlen(buf));
close(gpiofd);
}
int gpio_getfd(int gpio)
{
char in[3] = {0, 0, 0};
char buf[50];
int gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd < 0) {
fprintf(stderr, "Failed to open gpio %d value\n", gpio);
perror("gpio failed");
}
return gpiofd;
}
int gpio_read(int gpio)
{
char in[3] = {0, 0, 0};
char buf[50];
int nread, gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd < 0) {
fprintf(stderr, "Failed to open gpio %d value\n", gpio);
perror("gpio failed");
}
do {
nread = read(gpiofd, in, 1);
} while (nread == 0);
if(nread == -1){
perror("GPIO Read failed");
return -1;
}
close(gpiofd);
return atoi(in);
}
int gpio_write(int gpio, int val)
{
char buf[50];
int nread, ret, gpiofd;
sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
gpiofd = open(buf, O_RDWR);
if(gpiofd > 0) {
snprintf(buf, 2, "%d", val);
ret = write(gpiofd, buf, 2);
if(ret < 0) {
perror("failed to set gpio");
return 1;
}
close(gpiofd);
if(ret == 2) return 0;
}
return 1;
}
int gpio_select(int gpio)
{
char gpio_irq[64];
int ret = 0, buf, irqfd;
fd_set fds;
FD_ZERO(&fds);
snprintf(gpio_irq, sizeof(gpio_irq), "/sys/class/gpio/gpio%d/value", gpio);
irqfd = open(gpio_irq, O_RDONLY);
if(irqfd < 1) {
perror("Couldn't open the value file");
return -13;
}
// Read first since there is always an initial status
ret = read(irqfd, &buf, sizeof(buf));
while(1) {
FD_SET(irqfd, &fds);
ret = select(irqfd + 1, NULL, NULL, &fds, NULL);
if(FD_ISSET(irqfd, &fds))
{
FD_CLR(irqfd, &fds); //Remove the filedes from set
// Clear the junk data in the IRQ file
ret = read(irqfd, &buf, sizeof(buf));
return 1;
}
}
}
int main(int argc, char **argv) {
int gpio_pin = GPIO_PIN;
gpio_export(gpio_pin);
gpio_direction(gpio_pin, 1);
for(int i = 0; i < 5; i++) {
printf(">> GPIO %d ON\n", gpio_pin);
gpio_write(gpio_pin, 1);
sleep(1);
printf(">> GPIO %d OFF\n", gpio_pin);
gpio_write(gpio_pin, 0);
sleep(1);
}
return 0;
}
1.新建AndroidStdio工程
2.选择Native C++
3.定义工程名与包名(包名和工程名不可随便改动)
4.下一个界面出现C++ Standard 选择 C++11
5.新建工程如图所示,左上角可切换模式为project。
6.所需文件名在图示中所包含(CMakeLists.txt、MainActivity、activity_main.xml创建工程后存在,用附件对应同名文件覆盖即可;test_gpio.c放到cpp文件目录下)
附件.zip
7.直接运行,可以下载Scrcpy投屏文件,进行观察。
CAN总线是德国博世(BOSCH)公司从80年代初为解决现代汽车中众多的控制与测试仪器之间的数据交换而开发的一种串行数据通信协议,它是一种多主总线,通信介质可以是双绞线、同轴电缆或光导纤维。
YY3568开发板使用的CAN收发器是TCAN1044V-Q1,满足ISO 11898-2:2016高速CAN规范的物理层要求。TCAN1044V-Q1收发器支持传统CAN和CAN FD网络,高达8Mbps。TCAN1044V-Q1包括通过VIO端子实现的内部逻辑电平转换,支持将收发器IO直接连接1.8V、2.5V、3.3V或5.0V逻辑IO。
实物图引脚和原理图如下所示:
//can启动
YY3568:/ # ip link set can0 up
//can关闭
YY3568:/ # ip link set can0 down
//设置波特率250K
YY3568:/ # ip link set can0 type can bitrate 250000
//显示can详细信息
YY3568:/ # ip -details link show can0
3: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can <FD> state STOPPED (berr-counter tx 0 rx 0) restart-ms 0
rockchip_canfd: tseg1 1..128 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 2
rockchip_canfd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 2
clock 148500000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
//can发送标准数据帧 ID:12,data:DEADBAFF
YY3568:/ # cansend can0 12#DEADBAFF
//can发送远程帧 ID:12
YY3568:/ # cansend can0 12#R
//can发送扩展数据帧 ID:0000012,date:DEADBFFF
YY3568:/ # cansend can0 0000012#12345678
//can发送扩展远程帧 ID:0000012
YY3568:/ # cansend can0 0000012#R
//can数据接收
YY3568:/ # candump can0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#define can_up "ifconfig can0 up"//打开CAN0
#define can_down "ifconfig can0 down"//关闭CAN0
static void setCan0Bitrate(int nBitRate)
{
char anCanCommand[128] = {0,};
printf("- can0 down \n");
system(can_down);
printf("- can0 config bitrate %d\n", nBitRate);
sprintf(&anCanCommand[0], "ip link set can0 type can bitrate %d", nBitRate);
system(anCanCommand);
printf("- can0 up \n");
system(can_up);//关闭CAN设备,设置波特率后,重新打开CAN设备
}
int can0Init(int nBitRate)
{
int nCanFd;
struct sockaddr_can addr;
struct ifreq ifr;
setCan0Bitrate(nBitRate); //配置can0波特率
nCanFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字
printf("- can0 socket \n");
strcpy(ifr.ifr_name, "can0" );
ioctl(nCanFd, SIOCGIFINDEX, &ifr); //指定 can0 设备
printf("- can0 ioctl \n");
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(nCanFd, (struct sockaddr *)&addr, sizeof(addr));//将套接字与 can0 绑定
printf("- can0 binded \n");
return nCanFd;
}
int main()
{
int nCanFd, nbytes;
struct can_frame frame[2] = {{0}};
nCanFd = can0Init(1000000); //创建can实例化对象
frame[0].can_id = 0x666;
frame[0].can_dlc = 8;
frame[0].data[0] = 0x40;
frame[0].data[1] = 0x20;
frame[0].data[2] = 0x10;
frame[0].data[3] = 0x00;
frame[0].data[4] = 0x03;
frame[0].data[5] = 0x04;
frame[0].data[6] = 0x05;
frame[0].data[7] = 0x06;
for(int i=0;i<10;i++)
{
printf("- for enter: %d \n", i);
frame[0].data[7]++;
printf("send frames: 0x40 0x20 0x10 0x00 0x03 0x04 0x05 0x06 \n");
nbytes = write(nCanFd, &frame[0], sizeof(frame[0])); //发送 frame[0]
if(nbytes != sizeof(frame[0]))
{
printf("Send Error frame[0]\n!");
}
nbytes = read(nCanFd, &frame[1], sizeof(frame[1]));//接收总线上的报文保存在frame[1]中
printf("the nbytes:%d\n", nbytes);
printf("length:%d \n", sizeof(frame[1]));
printf("ID=0x%X DLC=%d\n", frame[1].can_id, frame[1].can_dlc);
printf("data0=0x%02x\n",frame[1].data[0]);
printf("data1=0x%02x\n",frame[1].data[1]);
printf("data2=0x%02x\n",frame[1].data[2]);
printf("data3=0x%02x\n",frame[1].data[3]);
printf("data4=0x%02x\n",frame[1].data[4]);
printf("data5=0x%02x\n",frame[1].data[5]);
printf("data6=0x%02x\n",frame[1].data[6]);
printf("data7=0x%02x\n",frame[1].data[7]);
sleep(1);
}
close(nCanFd);
return 0;
}
1.JAVA调用can的工程创建与GPIO类似,请读者自行创建,其工程目录下图。注意在创建包名的时候,请使用com.bin.cantest,因为下文的附件是按这个包名来构建的,所以读者应保持与该包名一致。
2.所需文件名在图示中所包含(CMakeLists.txt、MainActivity、activity_main.xml创建工程后存在,用附件对应同名文件覆盖即可;test_gpio.c放到cpp文件目录下)
附件can.zip
I2C总线在物理连接上由两条线组成:
这两条数据线需要接上拉电阻(如果不接上拉电阻,这两个引脚就处于悬空状态,然而悬空状态引脚的电平是无法确定的)。这样的话,总线在空闲的时候,SCL与SDA都会处于高电平状态。
YY3568有两个对外扩展的I2C接口,接口如下所示。具体引脚可以看背板的引脚丝印。
起始信号:通过前面的介绍,我们可以知道I2C总线在空闲时刻,SCL和SDA都是处于高电平的状态。在时钟线SCL保持高电平的期间,数据线SDA从高电平向低电平的跳变则称作I2C的起始信号。
停止信号:在时钟线SCL保持高电平的期间,数据线SDA从低电平向高电平的跳变则称作I2C的停止信号。
应答位信息:在8位数据传输结束后,发送端将SDA拉高,释放对SDA的控制,此时,接收端获得了对SDA的控制。如果拉低,就代表发出了应答信号(ACK),如果拉高,则代表发出了非应答信号(NACK),然后主机拉高SCL,将该信号发送回发送端
应答信号(ACK):在8位数据发送完成后,接收端把SDA拉低,则代表发送一个应答信号。它可以用来表示一个字节数据成功接收,也可以在主机为接收端时(主机进行读操作),未收到最后一个字节前,表示发送器可继续发送数据。
非应答信号(NACK):在8位数据发送完成后,接收端没有对SDA进行操作,则代表一个非应答信号。它可以用来表示一个字节数据没有成功传输,也可以在主机为接收端时(主机进行读操作),它收到最后一个字节后,主机(接收端)应发送一个NACK信号,以通知从机(发送端)结束数据发送,并释放数据总线,以便主机(接收端)发送一个停止信号停止通信。
我们在主机和从机建立通信之前,首先要确定两点。
第一个问题,我们在介绍I2C协议的时候说过,我们可以通过地址来区分不同的从设备。所以通过从设备地址我们可以区分我们需要通信的对象。
第二个问题,我们可以通过标志位来判断是要读还是写。如果置0那就是写,置1就是读
综合上述内容,I2C通信在起始信号发起后,首先会发送7位的地址和1位的读写标志位,一共8bit的数据(发送的流程和之前所说的数据传输一致,这里不再赘述)。用来确定通信的对象以及数据的流向。
上图就是I2C的完整通信流程了。结合上面所讲的内容,相信各位会很容易理解这张图。简单介绍一下上述流程,首先是起始信号发起,然后发送地址,从机给应答,然后发送8位数据,从机给应答,然后再发送8位数据,从机给应答。最后是结束信号发起,通信结束。
从图中可知时序如下:
I2C_TOOLS的下载链接:https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
作者在这里下载的是i2c-tools-4.3的版本
下载完将压缩包解压到tinkerR-Android9/external目录下(这是安卓系统代码,可以参考环境搭建这章获取下载方式)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
LOCAL_SRC_FILES := tools/i2cbusses.c tools/util.c
LOCAL_MODULE := i2c-tools
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:=tools/i2cdetect.c
LOCAL_MODULE:=i2cdetect
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:=tools/i2cget.c
LOCAL_MODULE:=i2cget
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:=tools/i2cset.c
LOCAL_MODULE:=i2cset
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:=tools/i2cdump.c
LOCAL_MODULE:=i2cdump
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)
LOCAL_PATH:= $(call my-dir)
################### i2c-tools #########################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE := i2c-tools
LOCAL_SRC_FILES := \
tools/i2cbusses.c \
tools/util.c \
lib/smbus.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
#LOCAL_CFLAGS := -g -Wall -Werror -Wno-unused-parameter
include $(BUILD_STATIC_LIBRARY)
################### i2cdetect #########################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE:=i2cdetect
LOCAL_SRC_FILES:= \
tools/i2cdetect.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES:= \
libc
LOCAL_STATIC_LIBRARIES := \
i2c-tools
LOCAL_CPPFLAGS += -DANDROID
include $(BUILD_EXECUTABLE)
#################### i2cget ###########################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE:=i2cget
LOCAL_SRC_FILES:= \
tools/i2cget.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES:= \
libc
LOCAL_STATIC_LIBRARIES := \
i2c-tools
LOCAL_CPPFLAGS += -DANDROID
include $(BUILD_EXECUTABLE)
##################### i2cset ##########################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE:=i2cset
LOCAL_SRC_FILES:= \
tools/i2cset.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES:= \
libc
LOCAL_STATIC_LIBRARIES := \
i2c-tools
LOCAL_CPPFLAGS += -DANDROID
include $(BUILD_EXECUTABLE)
##################### i2cdump #########################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE:=i2cdump
LOCAL_SRC_FILES:= \
tools/i2cdump.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES:= \
libc
LOCAL_STATIC_LIBRARIES := \
i2c-tools
LOCAL_CPPFLAGS += -DANDROID
include $(BUILD_EXECUTABLE)
################### i2ctransfer #######################
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_MODULE:=i2ctransfer
LOCAL_SRC_FILES:= \
tools/i2ctransfer.c
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES:= \
libc
LOCAL_STATIC_LIBRARIES := \
i2c-tools
LOCAL_CPPFLAGS += -DANDROID
include $(BUILD_EXECUTABLE)
./docker_builder/docker-builder-run.sh //进入docker环境
source build/envsetup.sh //配置环境变量
lunch xxx //这里xxx代指目标板的板级配置
cd external/i2c-tools-4.3/
mm //安卓源码单编命令
i2cdetect i2cdump i2cget i2cset i2ctransfer//此项只有4.x以上版本才有
YY3568:/ # i2cdetect -l
i2c-1 i2c rk3x-i2c I2C Adapter
i2c-6 i2c DP-AUX I2C Adapter
i2c-4 i2c rk3x-i2c I2C Adapter
i2c-0 i2c rk3x-i2c I2C Adapter
i2c-7 i2c DesignWare HDMI I2C Adapter
i2c-5 i2c rk3x-i2c I2C Adapter
YY3568:/ # i2cdetect -y 5 //此处5代表总线设备号 即检测的是i2c-5
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 51 52 53 -- -- -- -- -- -- -- -- -- -- -- -- //此处代表挂载的设备地址分别为0x50 0x51 0x52 0x53
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
YY3568:/ # i2cdump -f -y 5 0x50 //检测i2c-5总线上地址为0x50的从设备
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 50 eb f6 3f f9 83 32 31 31 30 39 33 33 39 38 32 P?????2110933982
10: 30 30 31 30 31 00 00 00 00 00 ff ff ff ff ff ff 00101...........
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
YY3568:/ # i2cdump -f -y 5 0x30 0xFC 0x3F //往i2c-5总线上地址为0x30的设备寄存器地址0xFC上写入0x3F
YY3568:/ # i2cget -f -y 5 0x50 0x01 //往i2c-5总线上地址为0x50的设备获取0x01寄存器的值
0xeb
//往i2c-1上0x30器件 0x3288寄存器 写入0xFA
YY3568:/ # i2ctransfer -f -y 5 w3@0x30 0x32 0x88 0xFA
//往i2c-1上0x30器件 0x3288寄存器 读取2个字节数据
YY3568:/ # i2ctransfer -f -y 5 w2@0x30 0x32 0x88 r2
// 1.用i2cdetect检测有多少组i2c总线在系统上
YY3568:/ # i2cdetect -l
i2c-1 i2c rk3x-i2c I2C Adapter
i2c-6 i2c DP-AUX I2C Adapter
i2c-4 i2c rk3x-i2c I2C Adapter
i2c-0 i2c rk3x-i2c I2C Adapter
i2c-7 i2c DesignWare HDMI I2C Adapter
i2c-5 i2c rk3x-i2c I2C Adapter
// 2.用i2cdetect检测挂在在i2c总线上的器件
YY3568:/ # i2cdetect -y 5 //此处5代表总线设备号 即检测的是i2c-5
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: 30 31 32 -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
// 3.用i2cdump查看器件所有寄存器的值
YY3568:/ # i2cdump -f -y 5 0x31 //检测i2c-5总线上地址为0x30的从设备
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
10: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
20: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
30: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
40: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
50: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
60: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
70: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
80: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
90: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
a0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
b0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
c0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
d0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
e0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
f0: 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c 6c llllllllllllllll
//4.用i2cset来设置单个寄存器的值
YY3568:/ # i2cdump -f -y 5 0x31 0xFC 0x3F //往i2c-5总线上地址为0x30的设备寄存器地址0xFC上写入0x3F
//5.用i2cget来获取单个寄存器的值
YY3568:/ # i2cget -f -y 5 0x31 0xFC //往i2c-5总线上地址为0x31的设备获取0xFC寄存器的值
0x3ff
//6.i2ctransfer使用,i2ctransfer支持16bit和32bit寄存器的读写,而i2cset和i2cget只支持8bit的寄存器
//往i2c-5上0x32器件 0x3288寄存器 写入0xFA
YY3568:/ # i2ctransfer -f -y 5 w3@0x32 0x32 0x88 0xFA
//往i2c-5上0x32器件 0x3288寄存器 读取2个字节数据
YY3568:/ # i2ctransfer -f -y 5 w2@0x32 0x32 0x88 r2
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
/* slave device所对应的I2C控制器的设备节点 */
#define I2C_DEVICE "/dev/i2c-5"
/* slave_device的I2C设备地址 */
#define SLAVE_DEVICE_ADDR 0x32
/*函数名:slave_device_write
**功能:向slave device写数据
**参数:fd:slave device对应I2C控制器设备节点的文件名
** dev_addr:slave device的I2C从设备地址
** reg_addr:slave device的寄存器地址
** data_buf:要向slave device写数据的数据buf
** len:要写多少个字节。本例中当前最大支持为8个字节
**返回值:负数表示操作失败,其他为成功
*/
int slave_device_write(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len)
{
int ret;
unsigned char msg_buf[9];
struct i2c_rdwr_ioctl_data data;
struct i2c_msg messages;
/* 1. 构建msg_buf*/
/* 1.1. 将要操作的寄存器首地址赋给要进行I2C数据通信的首字节数据 */
msg_buf[0] = reg_addr;
/* 1.2. 将要向slave_device写数据的数据buf赋在I2C数据通信中slave device寄存器的后面 */
if (len < 9) { /* 本demo最大支持一次向slave device写一页大小的8个字节数据 */
memcpy((void *) &msg_buf[1], data_buf, len); //第1位之后是数据
} else {
printf("This function supports up to 8 bytes at a time !!!\n");
return -1;
}
/* 2. 构建 struct i2c_msg messages */
/* 2.1. 赋值slave device的I2C从设备地址 */
messages.addr = dev_addr;
/* 2.2. 赋值flags为本次I2C通信完成写功能 */
messages.flags = 0;
/* 2.3. 赋值len为数据buf的长度 + slave device寄存器地址的数据长度 */
messages.len = len+1;
/* 2.4. 构建消息包的数据buf*/
messages.buf = msg_buf;
/* 3. 构建struct i2c_rdwr_ioctl_data data */
/* 3.1. 将准备好的消息包赋值给i2c_rdwr_ioctl_data中的msgs消息*/
data.msgs = &messages;
/* 3.2. 由于本次I2C通信只有写动作,所以消息数为1次 */
data.nmsgs = 1;
/* 4. 调用驱动层的读写组合的I2C数据传输 */
if(ioctl(fd, I2C_RDWR, &data) < 0)
{
printf("I2C_RDWR err \n");
return -1;
}
/* 5. 等待I2C总线写入完成 */
sleep(1);
return 0;
}
/*函数名:slave_device_read
**功能:从slave_device读数据
**参数:fd:slave_device对应I2C控制器设备节点的文件名
** dev_addr:slave_device的I2C从设备地址
** reg_addr:slave_device的寄存器地址
** data_buf:存放从slave_device读数据的buf
** len:要读多少个字节。
**返回值:负数表示操作失败,其他为成功
*/
int slave_device_read(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len)
{
int ret;
unsigned char msg_buf[9];
struct i2c_rdwr_ioctl_data data;
struct i2c_msg messages[2];
/* 1. 构建 struct i2c_msg messages */
/* 1.1. 构建第一条消息 messages[0] */
/* 1.1.1. 赋值slave_device的I2C从设备地址 */
messages[0].addr = dev_addr;
/* 1.1.2. 赋值flags为本次I2C通信完成写动作 */
messages[0].flags = 0;
/* 1.1.3. 赋值len为slave_device寄存器地址的数据长度是1 */
messages[0].len = 1;
/* 1.1.4. 本次写动作的数据是要读取slave_device的寄存器首地址*/
messages[0].buf = ®_addr;
/* 1.2. 构建第二条消息 messages[1] */
/* 1.2.1. 赋值slave_device的I2C从设备地址 */
messages[1].addr = dev_addr;
/* 1.1.2. 赋值flags为本次I2C通信完成读动作 */
messages[1].flags = I2C_M_RD;
/* 1.1.3. 赋值len为要读取slave_device寄存器数据长度len */
messages[1].len = len;
/* 1.1.4. 本次读动作的数据要存放的buf位置*/
messages[1].buf = data_buf;
/* 2. 构建struct i2c_rdwr_ioctl_data data */
/* 2.1. 将准备好的消息包赋值给i2c_rdwr_ioctl_data中的msgs消息*/
data.msgs = messages;
/* 2.2. 由于本次I2C通信既有写动作也有读动作,所以消息数为2次 */
data.nmsgs = 2;
/* 3. 调用驱动层的读写组合的I2C数据传输 */
if(ioctl(fd, I2C_RDWR, &data) < 0)
{
printf("I2C_RDWR err \n");
return -1;
}
/* 4. 等待I2C总线读取完成 */
sleep(1);
return 0;
}
int main()
{
int fd,i,ret=0;
unsigned char w_add=0x10;
/* 将要读取的数据buf*/
unsigned char rd_buf[8] = {0};
/* 要写的数据buf*/
unsigned char wr_buf[8] = {0};
printf("hello,this is I2C_RDWR i2c test \n");
/* 打开slave_device对应的I2C控制器文件 */
fd =open(I2C_DEVICE, O_RDWR);
if (fd< 0)
{
printf("open"I2C_DEVICE"failed \n");
}
/* 把要写入的数据写入到后面的buf中 */
for(i=0;i<8;i++)
wr_buf[i]=i;
/* 通过I2C_RDWR完成向slave_device读数据的功能 */
slave_device_write(fd,SLAVE_DEVICE_ADDR,w_add,wr_buf,8);
/* 通过I2C_RDWR完成向slave_device写数据的功能 */
slave_device_read(fd,SLAVE_DEVICE_ADDR,w_add,rd_buf,8);
for(i=0;i<8;i++)
{
printf("rd_buf is :%d\n",rd_buf[i]);
}
/* 完成操作后,关闭slave_device对应的I2C控制器的设备文件 */
close(fd);
return 0;
}
1.JAVA调用I2C的工程创建与GPIO类似,请读者自行创建,其工程目录下图。注意在创建包名的时候,请使用com.bin.i2ctest,因为下文的附件是按这个包名来构建的,所以读者应保持与该包名一致。
2.所需文件名在图示中所包含(CMakeLists.txt、MainActivity、activity_main.xml创建工程后存在,用附件对应同名文件覆盖即可;test_gpio.c放到cpp文件目录下)。
i2c附件.zip
YY3568支持五路UART,其中UART2作为DEBUG,其他四个作为普通串口使用。对应的串口引脚如下所示
//查看串口信息
YY3568:/ # stty -F /dev/ttyS4
speed 9600 baud; line = 0;
hupcl clocal
-brkint ixon -imaxbel
//设置串口波特率为115200
YY3568:/ # stty -F /dev/ttyS4 speed 115200
9600
YY3568:/ # stty -F /dev/ttyS4
speed 115200 baud; line = 0;
hupcl clocal
-brkint ixon -imaxbel
//发送数据
YY3568:/ # echo "Message From YY3568" > /dev/ttyS4
//接收数据
YY3568:/ # cat /dev/ttyS4
Message From My PC
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
int speed_arr[] = { B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300,
B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
19200, 9600, 4800, 2400, 1200, 300, };
void set_speed(int fd, int speed){
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
if (speed == name_arr[i]) {
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0) {
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}
/**
*@brief 设置串口数据位,停止位和效验位
*@param fd 类型 int 打开的串口文件句柄
*@param databits 类型 int 数据位 取值 为 7 或者8
*@param stopbits 类型 int 停止位 取值为 1 或者2
*@param parity 类型 int 效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0) {
perror("SetupSerial ");
printf("setup serial failurer\n");
return(-1);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return (-1);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (-1);
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (-1);
}
/* Set input parity option */
if (parity != 'n' && parity != 'N')
options.c_iflag |= INPCK;
tcflush(fd,TCIFLUSH);
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~(OPOST|ONLCR|OCRNL); /*Output*/
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | INLCR|IGNCR);
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (-1);
}
return (0);
}
int set_serial_port(int fd, speed_t speed, int databits,int stopbits,int parity)
{
int ret;
set_speed(fd, speed);
ret = set_Parity(fd, databits, stopbits, parity);
return ret;
}
int open_serial_port(char *dev_path)
{
int fd;
if (dev_path == NULL)
return -1;
fd=open(dev_path,O_RDWR | O_NOCTTY);
if(fd==-1)
{
printf("open serialport failed [%s]\n", dev_path);
perror("open serialport failed\n");
return(-1);
}
fcntl(fd, F_SETFL, 0);
return fd;
}
int init_uart(char * device_name, speed_t speed, int databits,int stopbits,int parity)
{
int ret;
int uart_fd = 0;
uart_fd = open_serial_port(device_name);
if(uart_fd<0)
{
printf("open serial port failed\n");
return -1;
}
ret=set_serial_port(uart_fd, speed, databits, stopbits, parity);
if(ret==-1)
{
printf("set serial port failed\n");
return -1;
}
return uart_fd;
}
int close_uart(int uart_fd)
{
if (uart_fd > 0)
close(uart_fd);
return 0;
}
int main()
{
int gblUartFd = -1;
int ret;
unsigned char buf[5]={0,};
gblUartFd = init_uart("/dev/ttyS4", 115200, 8, 1, 'N');
while(1)
{
ret = read(gblUartFd , buf, sizeof(char)*5);
printf("buf=%s",buf);
sleep(1);
}
close_uart(gblUartFd);
return 0;
}
在上方的创建
系统app demo
的基础上添加如下代码。
activity_main.xml
文件修改为如下代码。<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ScrollView android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
>
<TextView android:id="@+id/log"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:textSize="12sp"
android:textColor="#000000"
/>
</ScrollView>
<EditText android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
</LinearLayout>
MainActivity.java
文件修改为如下内容。package com.youyeetoo.demo;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.hardware.SerialManager;
import android.hardware.SerialPort;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity implements Runnable, TextView.OnEditorActionListener {
private static final String TAG = "SerialChat";
private TextView mLog;
private EditText mEditText;
private ByteBuffer mInputBuffer;
private ByteBuffer mOutputBuffer;
private SerialManager mSerialManager;
private SerialPort mSerialPort;
private boolean mPermissionRequestPending;
private static final int MESSAGE_LOG = 1;
@SuppressLint({"WrongConstant", "MissingInflatedId"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSerialManager = (SerialManager)getSystemService("serial");
mLog = (TextView)findViewById(R.id.log);
mEditText = (EditText)findViewById(R.id.message);
mEditText.setOnEditorActionListener(this);
if (false) {
mInputBuffer = ByteBuffer.allocateDirect(1024);
mOutputBuffer = ByteBuffer.allocateDirect(1024);
} else {
mInputBuffer = ByteBuffer.allocate(1024);
mOutputBuffer = ByteBuffer.allocate(1024);
}
}
@Override
public void onResume() {
super.onResume();
String[] ports = mSerialManager.getSerialPorts();
if (ports != null && ports.length > 0) {
try {
mSerialPort = mSerialManager.openSerialPort(ports[0], 115200);
if (mSerialPort != null) {
new Thread(this).start();
}
} catch (IOException e) {
}
}
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
if (mSerialPort != null) {
try {
mSerialPort.close();
} catch (IOException e) {
}
mSerialPort = null;
}
super.onDestroy();
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (/* actionId == EditorInfo.IME_ACTION_DONE && */ mSerialPort != null) {
try {
String text = v.getText().toString();
Log.d(TAG, "write: " + text);
byte[] bytes = text.getBytes();
mOutputBuffer.clear();
mOutputBuffer.put(bytes);
mSerialPort.write(mOutputBuffer, bytes.length);
} catch (IOException e) {
Log.e(TAG, "write failed", e);
}
v.setText("");
return true;
}
Log.d(TAG, "onEditorAction " + actionId + " event: " + event);
return false;
}
@Override
public void run() {
Log.d(TAG, "run");
int ret = 0;
byte[] buffer = new byte[1024];
while (ret >= 0) {
try {
Log.d(TAG, "calling read");
mInputBuffer.clear();
ret = mSerialPort.read(mInputBuffer);
Log.d(TAG, "read returned " + ret);
mInputBuffer.get(buffer, 0, ret);
} catch (IOException e) {
Log.e(TAG, "read failed", e);
break;
}
if (ret > 0) {
Message m = Message.obtain(mHandler, MESSAGE_LOG);
String text = new String(buffer, 0, ret);
Log.d(TAG, "chat: " + text);
m.obj = text;
mHandler.sendMessage(m);
}
}
Log.d(TAG, "thread out");
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_LOG:
mLog.setText(mLog.getText() + (String)msg.obj);
break;
}
}
};
}
IR基本原理, IR(Infrared Remote)即红外遥控。每按下一个键,即产生具有不同编码的数字脉冲,这种代码指令信号通过调制激励红外光二级管产生具有脉冲串的红外波,通过空间的传送到受控机内的遥控接收器。在接收过程中,红外波信号通过光学滤波器和光电二极管转换为电信号,此信号经过放大、检波、整形、解调、送到解码与接口电路,从而完成相应的遥控功能。YY3568的红外IR如下所示
原理图如下
//使能红外功能的打印
YY3568:/ # echo 1 > /sys/module/rockchip_pwm_remotectl/parameters/code_print
//按遥控器的按键 查看红外信号
YY3568:/ # dmesg
[ 260.913430] USERCODE=0xff00
[ 260.940511] RMC_GETDATA=f3
[ 261.215198] USERCODE=0xff00
[ 261.242199] RMC_GETDATA=f3
[ 261.552075] USERCODE=0xff00
[ 261.579073] RMC_GETDATA=f3
[ 277.059778] USERCODE=0xff00
[ 277.086760] RMC_GETDATA=e3
[ 277.296278] USERCODE=0xff00
[ 277.323284] RMC_GETDATA=e3
[ 277.516486] USERCODE=0xff00
[ 277.543501] RMC_GETDATA=e3
YY3568开发板引出了CSI接口,可以适配YY3568 Camera,如下图所示
上图与CAMERA的接口由I2C,GPIO,MIPI-CSI组成。其中I2C用于对Camera参数进行一些配置(曝光,增益,帧率等),GPIO用于Camera的上下电,MIPI用于图像数据的传输。
MIPI是2003年由ARM,TI等公司成立的联盟,目的是把手机内部的各种接口(摄像头CSI,显示屏DSI,射频/基带接口DigRF等)标准化,从而减少手机设计的复杂程度以及增加设计的灵活性,目前比较成熟的接口应用有DSI(显示接口),和CSI(摄像头接口),都具有比较复杂的协议结构,下图表示某一个SOC可以作为一个CSI的接收器,同时也可以作为一个DSI的输出器,其物理层使用到了D-PHY,目前新的物理层C-PHY也逐渐被采用,我们常说的Camera I2C接口在MIPI中有专门的一个CCI(Camera Control Interface)来对应
众所周知,Android的底层是Linux,在Linux下,所有外设都被看成一种特殊的文件,也就是一切皆文件,Linux中所有的外设均可像访问普通文件一样对其进行读写操作。V4L2的这套框架就为用户提供了视频采集的节点(文件),用户通过操作该文件就能进行视频采集的工作。用户态可以通过简单的ioctl就能去控制采集视频流。
下面例程中会使用到V4L2的框架去进行图像采集,如果需要通过shell来使用v4l2,需要安装v4l2-ctl工具。具体使用方式可以通过Video for Linux Two API Specification (linuxtv.org)来进行查阅,下文实例中会进行简单列举常用命令。
V4L2在include/linux/videodev.h文件中定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2的能力可在Linux内核编译阶段配置,默认情况下都有此开发接口。
在Linux中V4L2拍照的调用过程如下图所示。
//通过mipi-csi 接入的camera的节点为/dev/video0
//显示Camera所有信息(分辨率:Width/Height)
YY3568:/data # v4l2-ctl -d /dev/video0 --all
//显示Camera信息
YY3568:/data # v4l2-ctl -D
//获取支持的编码格式
YY3568:/data # v4l2-ctl --list-formats -d /dev/video0
//获取支持的camera设备
YY3568:/data # v4l2-ctl --list-devices -d /dev/video0
//Camera各种模式
YY3568:/data # v4l2-ctl -d /dev/video0 --list-ctrls
//获取支持的分辨率和编码格式
YY3568:/data # v4l2-ctl --list-formats-ext -d /dev/video0
//采集一张分辨率为3264x2448的图片,图片格式为NV12格式
YY3568:/ # v4l2-ctl --device /dev/video0 --set-fmt-video=width=3264,height=2448,pixelformat=NV12 --stream-mmap --stream-to=./3264x2448NV12.yuv --stream-count=1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#define VIDEO_DEV "/dev/video0"//摄像头设备名
#define DEBUG
buffer *user_buf = NULL;
static unsigned int n_buffer = 0;
static unsigned long file_length;
char picture_name[20] ="rk_picture";
int num = 0;
typedef struct _buffer
{
void *start;
size_t length;
}buffer;
/**
* @brief 打开摄像头设备函数
* @param None
* @retval fd 摄像头设备
*/
int open_camera_device(char * videoDev)
{
int fd;
/*1.打开设备文件。*/
if((fd = open(videoDev,O_RDWR | O_NONBLOCK)) < 0)
{
perror("Fail to open");
pthread_exit(NULL);
}
return fd;
}
/**
* @brief 初始化视频设备函数
* @param fd 摄像头设备
* @retval
*/
int init_camera_device(int fd)
{
struct v4l2_fmtdesc fmt;
struct v4l2_capability cap;
struct v4l2_format stream_fmt;
int ret;
/*2.取得设备的capability,查询视频设备驱动的功能
比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability*/
ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(ret < 0)
{
perror("FAIL to ioctl VIDIOC_QUERYCAP");
exit(EXIT_FAILURE);
}
//判断是否是一个视频捕捉设备
if(!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE))
{
perror("The Current device is not a video capture device\n");
exit(EXIT_FAILURE);
}
//判断是否支持视频流形式
if(!(cap.capabilities & V4L2_CAP_STREAMING))
{
perror("The Current device does not support streaming i/o\n");
exit(EXIT_FAILURE);
}
/*3.设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。*/
memset(&fmt,0,sizeof(fmt));
fmt.index = 0;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt)) == 0)
{
fmt.index ++ ;
#ifdef DEBUG
printf("{pixelformat = %c%c%c%c},description = '%s'\n",
fmt.pixelformat & 0xff,(fmt.pixelformat >> 8)&0xff,
(fmt.pixelformat >> 16) & 0xff,(fmt.pixelformat >> 24)&0xff,
fmt.description);
#endif
}
//设置摄像头采集数据格式,如设置采集数据的
//长,宽,图像格式(JPEG,YUYV,MJPEG等格式)
stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
stream_fmt.fmt.pix.width = 3264;//宽,必须是16的倍数
stream_fmt.fmt.pix.height = 2448;//高,必须是16的倍数
stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;//视频数据存储类型
stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
//设置当前驱动的频捕获格式
if(-1 == ioctl(fd,VIDIOC_S_FMT,&stream_fmt))
{
perror("Fail to ioctl");
exit(EXIT_FAILURE);
}
//计算图片大小
file_length = stream_fmt.fmt.pix.bytesperline * stream_fmt.fmt.pix.height;
//初始化视频采集方式(mmap)
init_mmap(fd);
return 0;
}
/**
* @brief 初始化视频采集方式(mmap)
* @param fd 摄像头设备
* @retval
*/
int init_mmap(int fd)
{
int i = 0;
struct v4l2_requestbuffers reqbuf;
/*4.向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers*/
bzero(&reqbuf,sizeof(reqbuf));
reqbuf.count = 4;//缓存数量,也就是说在缓存队列里保持多少张照片
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;//或V4L2_MEMORY_USERPTR
//申请视频缓冲区(这个缓冲区位于内核空间,需要通过mmap映射)
//这一步操作可能会修改reqbuf.count的值,修改为实际成功申请缓冲区个数
if(-1 == ioctl(fd,VIDIOC_REQBUFS,&reqbuf))
{
perror("Fail to ioctl 'VIDIOC_REQBUFS'");
exit(EXIT_FAILURE);
}
n_buffer = reqbuf.count;
#ifdef DEBUG
printf("n_buffer = %d\n",n_buffer);
#endif
user_buf = calloc(reqbuf.count,sizeof(*user_buf));//内存中建立对应空间
if(user_buf == NULL)
{
fprintf(stderr,"Out of memory\n");
exit(EXIT_FAILURE);
}
/*5.将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,
而不必去复制。mmap*/
for(i = 0; i < n_buffer; i ++)
{
struct v4l2_buffer buf;//驱动中的一帧
bzero(&buf,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
//查询申请到内核缓冲区的信息
if(-1 == ioctl(fd,VIDIOC_QUERYBUF,&buf)) //映射用户空间
{
perror("Fail to ioctl : VIDIOC_QUERYBUF");
exit(EXIT_FAILURE);
}
user_buf[i].length = buf.length;
user_buf[i].start =
mmap(
NULL,/*start anywhere*/
buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,buf.m.offset//通过mmap建立映射关系,返回映射区的起始地址
);
if(MAP_FAILED == user_buf[i].start)
{
perror("Fail to mmap");
exit(EXIT_FAILURE);
}
}
return 0;
}
int start_capturing(int fd)
{
unsigned int i;
enum v4l2_buf_type type;
/*6.将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer*/
for(i = 0;i < n_buffer;i ++)
{
struct v4l2_buffer buf;
bzero(&buf,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
//把数据从缓存中读取出来
if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))//申请到的缓冲进入列队
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
exit(EXIT_FAILURE);
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/*7.开始视频的采集。VIDIOC_STREAMON*/
if(-1 == ioctl(fd,VIDIOC_STREAMON,&type)) //开始捕捉图像数据
{
perror("Fail to ioctl 'VIDIOC_STREAMON'");
exit(EXIT_FAILURE);
}
return 0;
}
int mainloop(int fd)
{
int count = 2;
/*8.循环采集图片。*/
while(count-- > 0)
{
for(;;)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds);//将指定的文件描述符集清空
FD_SET(fd,&fds);//在文件描述符集合中增加新的文件描述符
/*Timeout*/
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1,&fds,NULL,NULL,&tv);//判断是否可读(即摄像头是否准备好),tv是定时
if(-1 == r)
{
if(EINTR == errno)
continue;
perror("Fail to select");
exit(EXIT_FAILURE);
}
if(0 == r)
{
fprintf(stderr,"select Timeout\n");
exit(EXIT_FAILURE);
}
if(read_frame(fd))//如果可读,执行read_frame ()函数,并跳出循环
break;
}
}
return 0;
}
//将采集好的数据放到文件中
int process_image(void *addr,int length)
{
FILE *fp;
char name[20];
sprintf(name,"%s%d.jpg",picture_name,num ++);
if((fp = fopen(name,"w")) == NULL)
{
perror("Fail to fopen");
exit(EXIT_FAILURE);
}
fwrite(addr,length,1,fp);
usleep(500);
fclose(fp);
return 0;
}
int read_frame(int fd)
{
struct v4l2_buffer buf;
unsigned int i;
bzero(&buf,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
/*9.出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF*/
if(-1 == ioctl(fd,VIDIOC_DQBUF,&buf))
{
perror("Fail to ioctl 'VIDIOC_DQBUF'");
exit(EXIT_FAILURE);
}
assert(buf.index < n_buffer);
{
#ifdef DEBUG
printf ("buf.index dq is %d,\n",buf.index);
#endif
}
//读取进程空间的数据到一个文件中
process_image(user_buf[buf.index].start,user_buf[buf.index].length);
/*10.将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF*/
if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))//把数据从缓存中读取出来
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
exit(EXIT_FAILURE);
}
return 1;
}
void stop_capturing(int fd)
{
enum v4l2_buf_type type;
/*11.停止视频的采集。VIDIOC_STREAMOFF*/
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == ioctl(fd,VIDIOC_STREAMOFF,&type))
{
perror("Fail to ioctl 'VIDIOC_STREAMOFF'");
exit(EXIT_FAILURE);
}
return;
}
void uninit_camera_device()
{
unsigned int i;
for(i = 0;i < n_buffer;i ++)
{
if(-1 == munmap(user_buf[i].start,user_buf[i].length))
{
exit(EXIT_FAILURE);
}
}
free(user_buf);
return;
}
void close_camera_device(int fd)
{
if(-1 == close(fd))
{
perror("Fail to close fd");
exit(EXIT_FAILURE);
}
return;
}
/**
* @brief 摄像头拍照函数
* @param void
* @retval Nono
*/
int main()
{
int camera_fd;
camera_fd = open_camera_device(VIDEO_DEV);
init_camera_device(camera_fd);
start_capturing(camera_fd);
num = 0;
mainloop(camera_fd);
stop_capturing(camera_fd);
uninit_camera_device(camera_fd);
close_camera_device(camera_fd);
printf("Camera get pic success!\n");
return 0;
}
板载NFC芯片采用意法半导体ST25DV04KC,如电路图所示
NFC天线接法图:
要驱动NFC,首先需要刷新的固件版本,编译时间为20240222开始的版本,若您手头开发板固件版本比这个新可不升级.
固件下载链接(这里版本是20240222):
https://pan.baidu.com/s/1nd8CwWPogZW50uopNN-rHQ?pwd=fkwg
刷固件请参考烧录系统部分:
http://wiki.youyeetoo.cn/zh/YY3568/Unpacking#androidtool
开发板端控制NFC对应APP
开发板在发安卓系统下开发,主要分2层设计:
jni 层负责I2C底层通讯和GPO中断监测.
java 层负责ST25DV各功能逻辑实现.
开发板端软件实现效果如图:
界面左侧上半部分:主要是实现开发板发数据给手机
fill data :填充测试数据
Clear All :清空
Send:开发板发数据给手机
界面左侧下半部分:主要是实现接收手机来的
clear:清空手机发来的数据
界面左侧底部:写标签
tag:youyeetoo.cn 把www.youyeetoo.cn写标签
tag:Android Start WeChat 写标签:实现手机刷一下开发板天线,手机自启微信APP.
界面右侧:是操作部分操作日志
开发板和手机之间FTM传输数据流程和协议如下:
1 发端-->接收端 发询问包
FE FE FE FE FLG1 00 00 00 14 L1 L2 L3 L4 FF FF FF FF 45 4E 44
其中
FLG1:表示数据发送方向,02开发板->手机,03:手机->开发板
L1 L2 L3 L4 :表示要发送指定的数据总长度(长度包括询问包所有长度+业务数据长度):L1长度的高位,L4是长度最低位,L2,L3依次类推
2 接收端--->发端 回应应答包
FE FE FE FE 04 00 00 00 14 FLG2 00 00 00 FF FF FF FF 45 4E 44
其中
FLG2:表示是否允许接受数据.E7表示允许接受,E9表示繁忙拒绝接受.
发端发数据流程:
1 发端先发询问包,询问目标是否可以接受指定长度数数据
2 若目标回应允许发送,进行步骤3,否则退出发送
3 数据分割拆包发送,知道发送完成.
接收端接收数据流程:
1 收到询问是否可以接受指定长度数据包,若允许发允许应答包给发,进入步骤2,否则发繁忙拒绝接受应答包给发端并结束接受.
2 等待并依次接受数据,直到接受到指定长度数据,组合成完整包.
由于代码比较多,这里不做展示,请下载源码进行查看和验证.
开发板端APP和源码下载:
编译好的APK:
http://dd.youyeetoo.cn:5000/sharing/tGYlp9C1C
源码:
http://dd.youyeetoo.cn:5000/sharing/prwGLKD2L
手机端NFC对应APP
手机APP参考意法半导体提供的开发库进行二次封装开发.
清空日志:清空上部分日志
发送:手机端向开发板发数据
填充数据:填充要发送数据
清空数据:清空要发送的数据
开发板和手机之间FTM传输数据流程和协议和上面开发板端相同.
由于代码比较多,这里不做展示,请下载源码进行查看和验证.
手机端APP和源码下载:
编译好的APK:
http://dd.youyeetoo.cn:5000/sharing/ALVndNaSa
源码:
http://dd.youyeetoo.cn:5000/sharing/RBNCwYbIu