本节提示
所需环境:ubuntu 16 、 18
工具:VScode、openwrt交叉编译工具
源码包:https://github.com/Lora-net/lora_gateway
https://github.com/TheThingsNetwork/packet_forwarder
2.4 替换LoRaWan 官方库底层spi驱动
在进入本章之前建议先自行学习这位博文:https://blog.csdn.net/iotisan/article/details/72633960
2.4.1 认识源码
通过源码分析和其他博主分析:我们这次的主要对:/lora_gateway/libloragw/src/loragw_spi.native.c
进行修改
#include <stdint.h> /* C99 types */
#include <stdio.h> /* printf fprintf */
#include <stdlib.h> /* malloc free */
#include <unistd.h> /* lseek, close */
#include <fcntl.h> /* open */
#include <string.h> /* memset */
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include "loragw_spi.h"
#include "loragw_hal.h"
/* -------------------------------------------------------------------------- */
/* --- PRIVATE MACROS ------------------------------------------------------- */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#if DEBUG_SPI == 1
#define DEBUG_MSG(str) fprintf(stderr, str)
#define DEBUG_PRINTF(fmt, args...) fprintf(stderr,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
#define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
#else
#define DEBUG_MSG(str)
#define DEBUG_PRINTF(fmt, args...)
#define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
#endif
/* -------------------------------------------------------------------------- */
/* --- PRIVATE CONSTANTS ---------------------------------------------------- */
#define READ_ACCESS 0x00
#define WRITE_ACCESS 0x80
#define SPI_SPEED 8000000
#define SPI_DEV_PATH "/dev/spidev0.0"
//#define SPI_DEV_PATH "/dev/spidev32766.0"
/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
/* SPI initialization and configuration */
int lgw_spi_open(void **spi_target_ptr) {
int *spi_device = NULL;
int dev;
int a=0, b=0;
int i;
/* check input variables */
CHECK_NULL(spi_target_ptr); /* cannot be null, must point on a void pointer (*spi_target_ptr can be null) */
/* allocate memory for the device descriptor */
spi_device = malloc(sizeof(int));
if (spi_device == NULL) {
DEBUG_MSG("ERROR: MALLOC FAIL\n");
return LGW_SPI_ERROR;
}
/* open SPI device */
dev = open(SPI_DEV_PATH, O_RDWR);
if (dev < 0) {
DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", SPI_DEV_PATH);
return LGW_SPI_ERROR;
}
/* setting SPI mode to 'mode 0' */
i = SPI_MODE_0;
a = ioctl(dev, SPI_IOC_WR_MODE, &i);
b = ioctl(dev, SPI_IOC_RD_MODE, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI max clk (in Hz) */
i = SPI_SPEED;
a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i);
b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI to MSB first */
i = 0;
a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i);
b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n");
close(dev);
free(spi_device);
return LGW_SPI_ERROR;
}
/* setting SPI to 8 bits per word */
i = 0;
a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i);
b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i);
if ((a < 0) || (b < 0)) {
DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n");
close(dev);
return LGW_SPI_ERROR;
}
*spi_device = dev;
*spi_target_ptr = (void *)spi_device;
DEBUG_MSG("Note: SPI port opened and configured ok\n");
return LGW_SPI_SUCCESS;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* SPI release */
int lgw_spi_close(void *spi_target) {
int spi_device;
int a;
/* check input variables */
CHECK_NULL(spi_target);
/* close file & deallocate file descriptor */
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
a = close(spi_device);
free(spi_target);
/* determine return code */
if (a < 0) {
DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI port closed\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Simple write */
int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data) {
int spi_device;
uint8_t out_buf[3];
uint8_t command_size;
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
if ((address & 0x80) != 0) {
DEBUG_MSG("WARNING: SPI address > 127\n");
}
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
if (spi_mux_mode == LGW_SPI_MUX_MODE1) {
out_buf[0] = spi_mux_target;
out_buf[1] = WRITE_ACCESS | (address & 0x7F);
out_buf[2] = data;
command_size = 3;
} else {
out_buf[0] = WRITE_ACCESS | (address & 0x7F);
out_buf[1] = data;
command_size = 2;
}
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.len = command_size;
k.speed_hz = SPI_SPEED;
k.cs_change = 0;
k.bits_per_word = 8;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Simple read */
int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data) {
int spi_device;
uint8_t out_buf[3];
uint8_t command_size;
uint8_t in_buf[ARRAY_SIZE(out_buf)];
struct spi_ioc_transfer k;
int a;
/* check input variables */
CHECK_NULL(spi_target);
if ((address & 0x80) != 0) {
DEBUG_MSG("WARNING: SPI address > 127\n");
}
CHECK_NULL(data);
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare frame to be sent */
if (spi_mux_mode == LGW_SPI_MUX_MODE1) {
out_buf[0] = spi_mux_target;
out_buf[1] = READ_ACCESS | (address & 0x7F);
out_buf[2] = 0x00;
command_size = 3;
} else {
out_buf[0] = READ_ACCESS | (address & 0x7F);
out_buf[1] = 0x00;
command_size = 2;
}
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k.tx_buf = (unsigned long) out_buf;
k.rx_buf = (unsigned long) in_buf;
k.len = command_size;
k.cs_change = 0;
a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
/* determine return code */
if (a != (int)k.len) {
DEBUG_MSG("ERROR: SPI READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI read success\n");
*data = in_buf[command_size - 1];
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Burst (multiple-byte) write */
int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) {
int spi_device;
uint8_t command[2];
uint8_t command_size;
struct spi_ioc_transfer k[2];
int size_to_do, chunk_size, offset;
int byte_transfered = 0;
int i;
/* check input parameters */
CHECK_NULL(spi_target);
if ((address & 0x80) != 0) {
DEBUG_MSG("WARNING: SPI address > 127\n");
}
CHECK_NULL(data);
if (size == 0) {
DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
return LGW_SPI_ERROR;
}
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare command byte */
if (spi_mux_mode == LGW_SPI_MUX_MODE1) {
command[0] = spi_mux_target;
command[1] = WRITE_ACCESS | (address & 0x7F);
command_size = 2;
} else {
command[0] = WRITE_ACCESS | (address & 0x7F);
command_size = 1;
}
size_to_do = size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k[0].tx_buf = (unsigned long) &command[0];
k[0].len = command_size;
k[0].cs_change = 0;
k[1].cs_change = 0;
for (i=0; size_to_do > 0; ++i) {
chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
offset = i * LGW_BURST_CHUNK;
k[1].tx_buf = (unsigned long)(data + offset);
k[1].len = chunk_size;
byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );
DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);
size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
}
/* determine return code */
if (byte_transfered != size) {
DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI burst write success\n");
return LGW_SPI_SUCCESS;
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* Burst (multiple-byte) read */
int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size) {
int spi_device;
uint8_t command[2];
uint8_t command_size;
struct spi_ioc_transfer k[2];
int size_to_do, chunk_size, offset;
int byte_transfered = 0;
int i;
/* check input parameters */
CHECK_NULL(spi_target);
if ((address & 0x80) != 0) {
DEBUG_MSG("WARNING: SPI address > 127\n");
}
CHECK_NULL(data);
if (size == 0) {
DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");
return LGW_SPI_ERROR;
}
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
/* prepare command byte */
if (spi_mux_mode == LGW_SPI_MUX_MODE1) {
command[0] = spi_mux_target;
command[1] = READ_ACCESS | (address & 0x7F);
command_size = 2;
} else {
command[0] = READ_ACCESS | (address & 0x7F);
command_size = 1;
}
size_to_do = size;
/* I/O transaction */
memset(&k, 0, sizeof(k)); /* clear k */
k[0].tx_buf = (unsigned long) &command[0];
k[0].len = command_size;
k[0].cs_change = 0;
k[1].cs_change = 0;
for (i=0; size_to_do > 0; ++i) {
chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;
offset = i * LGW_BURST_CHUNK;
k[1].rx_buf = (unsigned long)(data + offset);
k[1].len = chunk_size;
byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );
DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);
size_to_do -= chunk_size; /* subtract the quantity of data already transferred */
}
/* determine return code */
if (byte_transfered != size) {
DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI burst read success\n");
return LGW_SPI_SUCCESS;
}
}
2.4.2 找出我们需要修改的函数
熟悉linux的小伙伴,从以上源码不难看出,调用硬件spi,所需要的函数为:
int lgw_spi_open(void **spi_target_ptr)
int lgw_spi_close(void *spi_target)
int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data)
int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data)
int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size)
int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size)
**2.4.3修改int lgw_spi_open(void spi_target_ptr)
这个函数主要作用是:打开和设置硬件SPI 并且返回设备描述对象。在上一环节中,我们自写了SPI驱动并且已经设置好了我们所需的通信模式,因此只需将模拟SPI初始化函数写入即可。同时需要注意*spi_target_ptr = (void *)spi_device
。
在lora_reg.c有对spi_target_ptr的调用,主要作用是描述SPI设备。函数内容如下:
/* Concentrator connect */
int lgw_connect(bool spi_only, uint32_t tx_notch_freq) {
int spi_stat = LGW_SPI_SUCCESS;
uint8_t u = 0;
int x;
/* check SPI link status */
if (lgw_spi_target != NULL) {
DEBUG_MSG("WARNING: concentrator was already connected\n");
lgw_spi_close(lgw_spi_target);
}
/* open the SPI link */
spi_stat = lgw_spi_open(&lgw_spi_target);
if (spi_stat != LGW_SPI_SUCCESS) {
DEBUG_MSG("ERROR CONNECTING CONCENTRATOR\n");
return LGW_REG_ERROR;
}
if (spi_only == false ) {
/* Detect if the gateway has an FPGA with SPI mux header support */
/* First, we assume there is an FPGA, and try to read its version */
spi_stat = lgw_spi_r(lgw_spi_target, LGW_SPI_MUX_MODE1, LGW_SPI_MUX_TARGET_FPGA, loregs[LGW_VERSION].addr, &u);
if (spi_stat != LGW_SPI_SUCCESS) {
DEBUG_MSG("ERROR READING VERSION REGISTER\n");
return LGW_REG_ERROR;
}
if (check_fpga_version(u) != true) {
/* We failed to read expected FPGA version, so let's assume there is no FPGA */
DEBUG_PRINTF("INFO: no FPGA detected or version not supported (v%u)\n", u);
lgw_spi_mux_mode = LGW_SPI_MUX_MODE0;
} else {
DEBUG_PRINTF("INFO: detected FPGA with SPI mux header (v%u)\n", u);
lgw_spi_mux_mode = LGW_SPI_MUX_MODE1;
/* FPGA Soft Reset */
lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_FPGA, 0, 1);
lgw_spi_w(lgw_spi_target, lgw_spi_mux_mode, LGW_SPI_MUX_TARGET_FPGA, 0, 0);
/* FPGA configure */
x = lgw_fpga_configure(tx_notch_freq);
if (x != LGW_REG_SUCCESS) {
DEBUG_MSG("ERROR CONFIGURING FPGA\n");
return LGW_REG_ERROR;
}
}
在int lgw_connect(bool spi_only, uint32_t tx_notch_freq) 函数中对spi_target_ptr
进行了检空操作,然后作为SPI 设备描述符传给了lgw_spi_w
。到此得出结论,同时保证不破坏上级函数的调用因此对*spi_target_ptr
赋以非空值代码如下:
int lgw_spi_open(void **spi_target_ptr) {
*spi_target_ptr=“hello”;
spi_simulate_io_init();/模拟SPI初始化函数
return 0;
}
2.4.4 int lgw_spi_close(void spi_target)
SPI 关闭函数,在先前驱动程序中有自动关闭函数,因此在这里已经没有实质的功能,只需返回一个成功标志即可。
int lgw_spi_close(void *spi_target) {
int spi_device;
int a;
/* check input variables */
CHECK_NULL(spi_target);
/* close file & deallocate file descriptor */
spi_device = *(int *)spi_target; /* must check that spi_target is not null beforehand */
a = close(spi_device);
free(spi_target);
/* determine return code */
if (a < 0) {
DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n");
return LGW_SPI_ERROR;
} else {
DEBUG_MSG("Note: SPI port closed\n");
return LGW_SPI_SUCCESS;
}
}
修改代码如下:
int lgw_spi_close(void *spi_target) {
return LGW_SPI_SUCCESS;
}
(3)修改:
int lgw_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t data)
int lgw_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data)
int lgw_spi_wb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size)
int lgw_spi_rb(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, uint8_t address, uint8_t *data, uint16_t size)
对于这几个函数的修改,只需要替换为前一章的读写函数即可,这里不做过多的解说。
2.4.5 lora_gateway功能测试
(1)添加交叉编译工具:
打开lora_gateway主目录Makefile
:
ARCH ?=
CROSS_COMPILE ?=
export
### general build targets
all:
$(MAKE) all -e -C libloragw
$(MAKE) all -e -C util_pkt_logger
$(MAKE) all -e -C util_spi_stress
$(MAKE) all -e -C util_tx_test
$(MAKE) all -e -C util_lbt_test
$(MAKE) all -e -C util_tx_continuous
$(MAKE) all -e -C util_spectral_scan
clean:
$(MAKE) clean -e -C libloragw
$(MAKE) clean -e -C util_pkt_logger
$(MAKE) clean -e -C util_spi_stress
$(MAKE) clean -e -C util_tx_test
$(MAKE) clean -e -C util_lbt_test
$(MAKE) clean -e -C util_tx_continuous
$(MAKE) clean -e -C util_spectral_scan
### EOF
修改ARCH
CROSS_COMPILE
ARCH =mpis
CROSS_COMPILE =你的openwrt交叉编译工具存放路径
(2)编译
打开终端进入lora_gateway目录输入:
make
(3)测试
将步骤二无报错编译后的可执行文件整体打包至,MT7628中或者只复制loragw_reg
可执行文件。
说明:上层设置寄存器多调用loragw_reg.c中的函数,所以只要loragw_reg验证通过,整个程序就可以完美运行
测试结果如下:
2.5.5 整装测试
在这个环节我们需要源码:https://github.com/TheThingsNetwork/packet_forwarder 输入命令:
git clone https://github.com/TheThingsNetwork/packet_forwarder
在测试环节我们不要做过多修改,如果使用的到具体项目时,也比较容易修改,设计的代码量不是很大。接下来编译这个网关数据转发器。
同样进入主目录Makefile
增加添加编译工具,可参考本页:2.4.5 lora_gateway功能测试
设置。然后终端进入目录执行:make
即可。
注意:在编译过程中可能会报qsort_r()未定义;如果只是测试,可先将其屏蔽。
将编译以后的可执行文件整体复制或者单独复制lora_pkt_fwd
可执行文件到MT7628执行:
chmod 777 lora_pkt_fwd
./lora_pkt_fwd
使用终端发送数据得到结果(由于实验的时候openwrt系统没有进行时间校准,所以显示的时间不正确
):
至此sx1301的开源程序已经成功移植到mt7628 openwrt平台。后续将对离散的开源程序进行整合,特别是网关转发程序,很多内容对一般场景并不适用。