#include <jni.h>
|
#include <string>
|
#include <termios.h>
|
#include <unistd.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#include <fcntl.h>
|
#include <errno.h>
|
#include <android/log.h>
|
#include "include/gpiod.h"
|
|
#define TAG "SerialApp"
|
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
|
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
|
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
|
|
typedef struct comport_s
|
{
|
char devname[12];
|
unsigned int databit, parity, stopbit, flowctrl;
|
long baudrate;
|
|
int fd;
|
int frag_size;
|
}comport_t;
|
|
comport_t *adc_comport;
|
|
// 将int类型的波特率转换成可以识别的波特率
|
static speed_t getBaudRate(int baudRate){
|
switch (baudRate) {
|
case 0:
|
return B0;
|
case 50:
|
return B50;
|
case 75:
|
return B75;
|
case 110:
|
return B110;
|
case 134:
|
return B134;
|
case 150:
|
return B150;
|
case 200:
|
return B200;
|
case 300:
|
return B300;
|
case 600:
|
return B600;
|
case 1200:
|
return B1200;
|
case 1800:
|
return B1800;
|
case 2400:
|
return B2400;
|
case 4800:
|
return B4800;
|
case 9600:
|
return B9600;
|
case 19200:
|
return B19200;
|
case 38400:
|
return B38400;
|
case 57600:
|
return B57600;
|
case 115200:
|
return B115200;
|
case 230400:
|
return B230400;
|
case 460800:
|
return B460800;
|
case 500000:
|
return B500000;
|
case 576000:
|
return B576000;
|
case 921600:
|
return B921600;
|
case 1000000:
|
return B1000000;
|
case 1152000:
|
return B1152000;
|
case 1500000:
|
return B1500000;
|
case 2000000:
|
return B2000000;
|
case 2500000:
|
return B2500000;
|
case 3000000:
|
return B3000000;
|
case 3500000:
|
return B3500000;
|
case 4000000:
|
return B4000000;
|
default:
|
return -1;
|
}
|
}
|
|
unsigned short crc_modbus(unsigned char *ptr, int len)
|
{
|
unsigned int i;
|
unsigned short crc = 0xFFFF; //crc16位寄存器初始值
|
|
while(len--)
|
{
|
crc ^= *ptr++;
|
for (i = 0; i < 8; ++i)
|
{
|
if (crc & 1)
|
crc = (crc >> 1) ^ 0xA001; //多项式 POLY(0x8005)的高低位交换值,这是由于其模型的一>些参数决定的
|
else
|
crc = (crc >> 1);
|
}
|
}
|
|
return crc;
|
}
|
|
void calculate(char *result, unsigned char *data, int len)
|
{
|
int i = 0;
|
unsigned char temp[6];
|
char info[8];
|
for(i=0; i<len; i++)
|
{
|
if(data[i] == 0xAA && data[i+1] == 0x55)
|
{
|
memset(temp, 0, sizeof(temp));
|
if( (data[i+2] == 0x01 || data[i+2] == 0x02) && \
|
(data[i+3] >= 0x00 && data[i+3] <= 0x03))
|
{
|
// temp用于进行CRC校验
|
temp[0] = data[i];
|
temp[1] = data[i+1];
|
temp[2] = data[i+2];
|
temp[3] = data[i+3];
|
temp[4] = data[i+4];
|
temp[5] = data[i+5];
|
//CRC,if right continue, else break.
|
uint16_t crc = crc_modbus(temp, 6);
|
printf("received buffer crc = %04X\n", crc);
|
if(data[i+6] != (crc&0xFF) || data[i+7] != ((crc>>8)&0xFF))
|
{
|
LOGE("read failed: CRC failure. received CRC: %02X %02X, calculated CRC:%02X %02X\n", \
|
temp[4], temp[5], (crc&0xFF), ((crc>>8)&0xFF));
|
continue ;
|
}
|
|
// calculate 43 02 ==> 0243
|
memset(info, 0, sizeof(info));
|
snprintf(info, sizeof(info), "%02x%02x", data[i+5], data[i+4]);
|
int length = sizeof(info) / sizeof(info[0]);
|
long long sum = 0;
|
printf("info=%s\n", info);
|
for(int j=0; j<strlen(info); j++)
|
{
|
char a = info[j];
|
if(a >= '0' && a <= '9')
|
a = a - '0';
|
else if(a >= 'A' && a <= 'F')
|
a = a - 'A' + 10;
|
else if(a >= 'a' && a <= 'f')
|
a = a - 'a' + 10;
|
int n = a << (4*(3-j));
|
sum += n;
|
}
|
|
char temp_res[350];
|
memset(temp_res, 0, sizeof(temp_res));
|
if(data[i+2] == 0x01)
|
{
|
strcat(result, "电流-");
|
snprintf(temp_res, sizeof(temp_res), "通道%d=%.2fmA ", data[i+3], (double)sum*0.01);
|
}
|
else if(data[i+2] == 0x02)
|
{
|
strcat(result, "电压-");
|
snprintf(temp_res, sizeof(temp_res), "通道%d=%.3fV ", data[i+3], (double)sum*0.001);
|
}
|
strcat(result, temp_res);
|
}
|
}
|
}
|
}
|
|
extern "C"
|
JNIEXPORT jint JNICALL
|
Java_com_example_serial_AdcControl_openComport(JNIEnv *env, jclass clazz, jstring path,
|
jlong baud_rate, jint data_bits, jint parity,
|
jint stop_bits, jint flow_control) {
|
// TODO: implement openComport()
|
speed_t speed;
|
struct termios old_cfg, new_cfg;
|
int old_flags;
|
long temp;
|
|
adc_comport = (comport_t *) malloc(sizeof(comport_t));
|
if (adc_comport == NULL) {
|
LOGE("Failed to allocate memory for comport structure.");
|
return -1;
|
}
|
memset(adc_comport, 0, sizeof(adc_comport));
|
|
// 检查波特率是否合法
|
{
|
speed = getBaudRate(baud_rate);
|
if (speed == -1)
|
{
|
LOGE("Invalid buad rate");
|
return -1;
|
}
|
}
|
//
|
// // 打开串口设备
|
{
|
jboolean iscopy;
|
// 获取String字符串path中的UTF8编码,并将其转换成C中的字符串
|
const char* devname = (*env).GetStringUTFChars(path, &iscopy);
|
LOGD("Opening serial port %s with flags 0x%x", devname, O_RDWR);
|
strncpy(adc_comport->devname, devname, 12);
|
adc_comport->baudrate = baud_rate;
|
adc_comport->fd = -1;
|
adc_comport->frag_size = 128;
|
adc_comport->databit = data_bits;
|
adc_comport->parity = parity;
|
adc_comport->flowctrl = flow_control;
|
adc_comport->stopbit = stop_bits;
|
|
if( !strstr(adc_comport->devname, "tty") )
|
{
|
LOGE("Open Not a tty device \" %s\" \n", adc_comport->devname);
|
return -2;
|
}
|
|
adc_comport->fd = open(adc_comport->devname, O_RDWR);
|
if ( adc_comport->fd < 0 )
|
{
|
if( strstr(strerror(errno), "Permission denied") != nullptr )
|
return 1;
|
LOGE("open port failed:%s\n", strerror(errno));
|
return -3;
|
}
|
LOGD("Open device %s", adc_comport->devname);
|
// 释放JNI字符串的UTF8编码
|
(*env).ReleaseStringUTFChars(path, devname);
|
|
if ( 0 != tcgetattr(adc_comport->fd, &old_cfg))
|
{
|
LOGE("open port failed:%s\n", strerror(errno));
|
return -6;
|
}
|
}
|
//
|
{
|
LOGD("starting configuring serial port");
|
|
old_cfg.c_cflag &= ~CSIZE;
|
old_cfg.c_cflag |= CREAD | CLOCAL;
|
old_cfg.c_lflag &= ~ICANON;
|
old_cfg.c_lflag &= ~ECHO;
|
old_cfg.c_lflag &= ~ECHOE;
|
old_cfg.c_lflag &= ~ECHONL;
|
old_cfg.c_lflag &= ~ISIG;
|
old_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
|
old_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
|
old_cfg.c_oflag &= ~OPOST;
|
old_cfg.c_oflag &= ~ONLCR;
|
|
|
switch (adc_comport->databit)
|
{
|
case 7:
|
old_cfg.c_cflag |= CS7;
|
break;
|
case 6:
|
old_cfg.c_cflag |= CS6;
|
break;
|
case 5:
|
old_cfg.c_cflag |= CS5;
|
break;
|
case 8:
|
old_cfg.c_cflag |= CS8;
|
break;
|
}
|
|
switch(adc_comport->parity)
|
{
|
case 1:
|
old_cfg.c_cflag |= (PARENB | PARODD);
|
old_cfg.c_cflag |= (INPCK | ISTRIP);
|
break;
|
case 2:
|
old_cfg.c_cflag |= PARENB;
|
old_cfg.c_cflag &= ~PARODD;
|
old_cfg.c_cflag |= (INPCK | ISTRIP);
|
break;
|
case 0:
|
old_cfg.c_cflag &= ~PARENB;
|
break;
|
}
|
|
if(1 != adc_comport->stopbit)
|
{
|
old_cfg.c_cflag |= CSTOPB;
|
}
|
else
|
{
|
old_cfg.c_cflag &= ~CSTOPB;
|
}
|
|
// set flow control
|
// 1: software control; 2:hardware control; 0: none
|
switch(adc_comport->flowctrl)
|
{
|
case 1:
|
old_cfg.c_cflag &= ~(CRTSCTS);
|
old_cfg.c_iflag |= (IXON | IXOFF);
|
break;
|
case 2:
|
old_cfg.c_cflag |= CRTSCTS;
|
old_cfg.c_iflag &= ~(IXON | IXOFF);
|
break;
|
case 0:
|
old_cfg.c_cflag &= ~(CRTSCTS);
|
break;
|
}
|
|
|
// set baudRate
|
cfsetispeed(&old_cfg, speed);
|
cfsetospeed(&old_cfg, speed);
|
|
old_cfg.c_cc[VTIME] = 10;
|
old_cfg.c_cc[VMIN] = 0;
|
|
|
// 将设置的串口参数应用到串口上,TCSANOW表示立即生效,(TCSADRAIN:在所有输出都被传输后生效;TCSAFLUSH:在所有输出都被传输后生效,同时丢弃所有未读取的输入)
|
if(tcsetattr(adc_comport->fd, TCSANOW, &old_cfg))
|
{
|
LOGE("tcsetattr() failed:%s", strerror(errno));
|
close(adc_comport->fd);
|
return -1;
|
}
|
LOGD("Connected device \" %s \" successfully\n", adc_comport->devname);
|
LOGD("port:%s, databit:%d, stopbit:%d, parity:%d, flowctl:%d", adc_comport->devname, adc_comport->databit, adc_comport->stopbit, adc_comport->parity, adc_comport->flowctrl);
|
}
|
|
return 0;
|
}
|
extern "C"
|
JNIEXPORT jint JNICALL
|
Java_com_example_serial_AdcControl_closeComport(JNIEnv *env, jclass clazz) {
|
// TODO: implement closeadc_comport()
|
if ( !adc_comport )
|
{
|
LOGE("%s() get invalid input arguments.\n", __FUNCTION__ );
|
return -1;
|
}
|
|
if (adc_comport->fd >= 0)
|
{
|
close(adc_comport->fd);
|
LOGD("close device \" %s \" successfully\n", adc_comport->devname);
|
}
|
adc_comport->fd = -1;
|
free(adc_comport);
|
adc_comport = NULL;
|
return 0;
|
}
|
extern "C"
|
JNIEXPORT jint JNICALL
|
Java_com_example_serial_AdcControl_sendToPort(JNIEnv *env, jclass clazz, jstring operate,
|
jstring channel) {
|
// TODO: implement sendToPort()
|
char *operator_native;
|
char *channel_native;
|
unsigned char data[6] = {0xAA, 0x55};
|
int len = 2; //data的长度
|
int rv = -1;
|
int i;
|
unsigned int byte;
|
|
operator_native = (char *)env->GetStringUTFChars(operate, 0);
|
channel_native = (char *)env->GetStringUTFChars(channel, 0);
|
if(!operator_native || !channel_native)
|
{
|
LOGE("Invalid parameters\n");
|
return -1;
|
}
|
|
sscanf(operator_native, "%02X", &byte);
|
data[len++] = (unsigned char)byte;
|
|
sscanf(channel_native, "%02X", &byte);
|
data[len++] = (unsigned char)byte;
|
|
// 计算CRC16-MODBUS校验码
|
uint16_t crc = crc_modbus(data, len);
|
data[len++] = crc & 0xFF;
|
data[len++] = (crc >> 8) & 0xFF;
|
|
rv = write(adc_comport->fd, data, len);
|
if(rv < 0)
|
{
|
LOGE("write can data failed:%s", strerror(errno));
|
close(adc_comport->fd);
|
return -2;
|
}
|
LOGD("write %d bytes can data success", rv);
|
for(i = 0; i < rv; i++)
|
{
|
LOGD("%02X ", data[i]);
|
}
|
|
(*env).ReleaseStringUTFChars(operate, operator_native);
|
(*env).ReleaseStringUTFChars(channel, channel_native);
|
return 0;
|
}
|
|
extern "C"
|
JNIEXPORT jstring JNICALL
|
Java_com_example_serial_AdcControl_recvFromPort(JNIEnv *env, jclass clazz, jint len, jint timeout) {
|
// TODO: implement recvFromPort()
|
int rv = 0;
|
int iRet;
|
fd_set rdfds, exfds;
|
struct timeval stTime;
|
unsigned char nativeMsg[len];
|
|
memset(nativeMsg, 0, len);
|
|
if( !adc_comport || len <= 0 )
|
{
|
LOGE("Invalid parameter.\n");
|
return nullptr;
|
}
|
if (adc_comport->fd == -1)
|
{
|
LOGE("Serail not connected.\n");
|
return nullptr;
|
}
|
|
FD_ZERO(&rdfds);
|
FD_ZERO(&exfds);
|
FD_SET(adc_comport->fd, &rdfds);
|
FD_SET(adc_comport->fd, &exfds);
|
|
if (0xFFFFFFFF != timeout)
|
{
|
stTime.tv_sec = (time_t) (timeout / 1000);
|
stTime.tv_usec = (long)(1000 * (timeout % 1000));
|
|
iRet = select(adc_comport->fd + 1, &rdfds, 0, &exfds, &stTime);
|
if (0 == iRet)
|
{
|
return nullptr;
|
}
|
else if (0 < iRet)
|
{
|
if (0 != FD_ISSET(adc_comport->fd, &exfds))
|
{
|
LOGE("Error checking recv status.\n");
|
return nullptr;
|
}
|
|
if (0 == FD_ISSET(adc_comport->fd, &rdfds))
|
{
|
LOGE("No incoming data.\n");
|
return nullptr;
|
}
|
}
|
else
|
{
|
if (EINTR == errno)
|
{
|
LOGE("catch interrupt signal.\n");
|
}
|
else
|
{
|
LOGE("Check recv status failure.\n");
|
}
|
return nullptr;
|
}
|
}
|
|
usleep(10000); /* sleep for 10ms for data incoming */
|
|
// Get data from Comport
|
LOGD("start read");
|
iRet = read(adc_comport->fd, nativeMsg, len);
|
if (0 > iRet)
|
{
|
return nullptr;
|
}
|
LOGD("Receive {%d} bytes data successfully\n", iRet);
|
for(int i=0; i<iRet; i++)
|
{
|
LOGD("%02X", nativeMsg[i]);
|
}
|
|
char *tempHex = (char *) malloc(iRet * 3);
|
char *recvHex = (char *) malloc(iRet * 3 + 12);
|
if (tempHex == NULL || recvHex == NULL)
|
{
|
LOGE("Memory allocation failed: %s", strerror(errno));
|
return nullptr;
|
}
|
memset(tempHex, 0, sizeof (tempHex));
|
memset(recvHex, 0, sizeof (recvHex));
|
for (int i = 0; i < iRet; i++) {
|
sprintf(tempHex + i * 3, "%02X ", nativeMsg[i]);
|
}
|
strcat(recvHex, "Hex result: ");
|
strcat(recvHex, tempHex);
|
|
char recvalue[512];
|
memset(recvalue, 0, sizeof (recvalue));
|
calculate(recvalue, nativeMsg, iRet);
|
strcat(recvalue, recvHex);
|
LOGD("%s", recvalue);
|
return env->NewStringUTF(recvalue);
|
}
|