目錄1.前言2.windows ?串口通信API3.JAVA-JNI ?java程序調(diào)用C++程序4.C/C++封裝 ?動(dòng)態(tài)運(yùn)行庫一、前言?? https://www.cnblogs.com/kadcyh/p/14389710.html 寫這個(gè)博客主要是因?yàn)樽约合胗胘ava寫一個(gè)小小的后端服務(wù)器,其中要處理由51單片機(jī)傳送來的一些數(shù)據(jù)。單片機(jī)的數(shù)據(jù)由USB轉(zhuǎn)串口發(fā)送至上位機(jī),要處理這些數(shù)據(jù),就會(huì)用到windows提供一些API( Application Programming Interface,應(yīng)用程序接口 )。java在安裝了相關(guān)的包后,比如JNative.jar,可以直接用該包提供的接口來進(jìn)行調(diào)用windowsAPI。但是我才接觸java。而且整個(gè)作業(yè),我僅僅只要一部分來處理這個(gè)數(shù)據(jù)。安裝一個(gè)java包,實(shí)在是大可不必,所以就用C/C++來寫一個(gè)的終端,封裝一下放在java程序里面。 二、windows串口通信API??2.1 ? 工具:VC++6.0 ??2.2 ? 概述:windows操作系統(tǒng)的設(shè)備無關(guān)性將所有的外設(shè)都當(dāng)做文件來操作,那么我們寫串口通信就直接將串口當(dāng)做文件來讀寫。那么我們打開串口后一定要記得關(guān)閉,這個(gè)很重要 ??2.3 ? 串口通信程序概述 2.3.1 打開/關(guān)閉串口HANDLE WINAPI CreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, //共享模式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全屬性 DWORD dwCreationDisposition, //指定文件的動(dòng)作 DWORD dwFlagsAndAttributes, //文件屬性---不指定就默認(rèn)為同步IO HANDLE hTemplateFile //模板文件 ); BOOL WINAPI CloseHandle( HANDLE hObject ); 返回值:一個(gè)串口的句柄。 2.3.2 配置串口通信
BOOL WINAPI GetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts ); BOOL WINAPI SetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts ); COMMTIMEOUTS: typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout; /* 設(shè)置兩個(gè)字符之前的最大讀取時(shí)間 */
DWORD ReadTotalTimeoutMultiplier; /* 設(shè)置每個(gè)字符的讀取時(shí)間 */
DWORD ReadTotalTimeoutConstant; /* 設(shè)置所有字符讀取的最大時(shí)間 */
DWORD WriteTotalTimeoutMultiplier; /* 設(shè)置每個(gè)字符的寫入時(shí)間 */
DWORD WriteTotalTimeoutConstant; /* 設(shè)置所有字符的寫入時(shí)間 */
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
BOOL WINAPI GetCommState( HANDLE hFile, LPDCB lpDCB ); BOOL WINAPI SetCommState( HANDLE hFile, LPDCB lpDCB ); DCB數(shù)據(jù)結(jié)構(gòu)我們初級學(xué)者需要關(guān)注:波特率、校驗(yàn)位、停止位、發(fā)送數(shù)據(jù)位數(shù)。
BOOL WINAPI SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue );
BOOL WINAPI ReadFile( HANDLE hFile, LPVOID lpBuffer, //存放數(shù)據(jù)的緩沖區(qū) DWORD nNumberOfBytesToRead, //一次想要讀入的數(shù)據(jù)長度 LPDWORD lpNumberOfBytesRead, //實(shí)際讀入的數(shù)據(jù)長度 LPOVERLAPPED lpOverlapped ); BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ); 2.3.3 串口緩沖區(qū)配置在程序運(yùn)行的時(shí)候,應(yīng)該保證設(shè)置的緩沖區(qū)是“干凈”的。所以在讀數(shù)據(jù)或者寫數(shù)據(jù)之前,可以先清空一下緩沖區(qū)。 //清空緩沖區(qū) BOOL WINAPI PurgeComm( HANDLE hFile, DWORD dwFlags ); //清除錯(cuò)誤 BOOL WINAPI ClearCommError( HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat ); 2.4完整的讀串口代碼#include <stdio.h>`
#include <windows.h>
int Comm(int nBaud,int parity,int bytesize,int stopbits,int accdatalength,char rBuf[])
{ //緩沖區(qū)
DWORD rSize = 0;
DWORD dwError; //清除錯(cuò)誤
COMSTAT cs;
COMMTIMEOUTS timeouts; //超時(shí)數(shù)據(jù)結(jié)構(gòu)
DCB dcb; //串口通信配置文件---用LPDCB類型會(huì)報(bào)錯(cuò)
HANDLE hCom; //串口的句柄(實(shí)例)| the instance of com
hCom = CreateFile("COM3", //串口的名字
GENERIC_READ | GENERIC_WRITE, //串口打開方式
0, //共享方式
NULL, //安全屬性
OPEN_EXISTING, //指定文件的動(dòng)作
0, //文件屬性---不指定就默認(rèn)為同步IO
NULL //指向模板文件的句柄
);
if(hCom == INVALID_HANDLE_VALUE)
{
return -1;
}
///////////////////////////////////////
//同步IO需要設(shè)置讀數(shù)據(jù)超時(shí)
//////////////////////////////////////
if(!(GetCommTimeouts(hCom,&timeouts))) //獲的COMMTIMEOUTS結(jié)構(gòu)失?。? {
CloseHandle(hCom);
}
timeouts.ReadIntervalTimeout = 1000; //讀取每個(gè)字符之間的超時(shí)
timeouts.ReadTotalTimeoutMultiplier = 500; //讀取一個(gè)字符的超時(shí)
timeouts.ReadTotalTimeoutConstant=5000; //固定總超時(shí)
timeouts.WriteTotalTimeoutConstant = 0; //寫入字符之間超時(shí)
timeouts.WriteTotalTimeoutMultiplier = 0; //寫入字符總超時(shí)
if(!(SetCommTimeouts(hCom,&timeouts))) //設(shè)置COMMTIMEOUTS結(jié)構(gòu)失敗
{
CloseHandle(hCom);
}
////////////////////////////////////////
//設(shè)置緩沖區(qū)大小
///////////////////////////////////////
if(!SetupComm(hCom,500,500)) //設(shè)置讀寫緩沖區(qū)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//設(shè)置波特率等其它讀文件配置
/////////////////////////////////////
if(GetCommState(hCom,&dcb)==0) //獲得DCB數(shù)據(jù)失敗
{
CloseHandle(hCom);
}
//dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = nBaud; //波特率為4800
dcb.Parity = parity; //校驗(yàn)方式為無校驗(yàn)
dcb.ByteSize = bytesize; //數(shù)據(jù)位為8位
dcb.StopBits = stopbits; //停止位為1位
if (!SetCommState(hCom,&dcb)) //設(shè)置串口通信配置項(xiàng)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//清除緩沖區(qū)
/////////////////////////////////////
PurgeComm(hCom,PURGE_RXCLEAR|PURGE_TXCLEAR);
///////////////////////////////////////
//清除錯(cuò)誤
///////////////////////////////////////
if(!(ClearCommError(hCom,&dwError,&cs)))
{
CloseHandle(hCom);
}
////////////////////////////////////////////
//開始讀取緩沖口的數(shù)據(jù)
///////////////////////////////////////////
//讀取串口數(shù)據(jù)
if(INVALID_HANDLE_VALUE != hCom)
{
WriteFile
ReadFile(hCom,rBuf,accdatalength,&rSize,NULL);
printf("%d \n",rSize);
if(rSize)
{
CloseHandle(hCom);
return 1;
}
else
{
CloseHandle(hCom);
return 0;
}
}
else
{
CloseHandle(hCom);
return 2;
}
}
void main()
{
int i;
char buf[18] = {0};
int a,b,c,d,e;
printf("請輸入相關(guān)參數(shù):");
scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
while(1)
{
if(Comm(a,b,c,d,e,buf)==1)
{
for(i=0;i<17;i++)
{
printf("%c ",buf[i]);
}
printf("\n");
}
else
{
printf("%d \n",Comm(a,b,c,d,e,buf));
break;
}
}
}運(yùn)行結(jié)果
三、JAVA-JNI ?java程序調(diào)用C++程序??參考博客 工具:eclipse 3.1??首先在新建一個(gè)類,類的名字隨意。最好加上main()函數(shù)方便我們進(jìn)行模塊調(diào)試。在這個(gè)類里面。在這個(gè)類里面我們要自己定義將會(huì)用C/C++實(shí)現(xiàn)的函數(shù)。并且必須用到j(luò)ava提供的 System.loadLibrary()函數(shù)。如下: public class Com {
static {
System.loadLibrary("CommDLL"); //靜態(tài)語句塊,保證在創(chuàng)建該類的時(shí)候,該方法必須且只會(huì)調(diào)用一次。參數(shù)就是編寫的動(dòng)態(tài)庫名稱
}
public native int Comm(int nBaud,int parity,int bytesize,int stopbits,int accdatalength,String ComName,char[] rBuf);//將要用C/C++實(shí)現(xiàn)的函數(shù)
public static void main(String[] args) {
int term = new Com().Comm(4800, 0, 8, 0, 17,"COM3", Buffer);
}
}` 3.2??找到剛編寫的java源程序文件所在的位置。然后如下操作: ??進(jìn)入控制臺(tái): ??輸入 javac -d ./ Com.java 。即是根據(jù)將java文件經(jīng)過編譯生成二進(jìn)制文件(class文件)
??輸入 javac -h ./ Com.java 生成相關(guān)的頭文件。
??注:如果報(bào)錯(cuò):'javac不是內(nèi)部或外部命令,也不是可運(yùn)行的程序或批處理文件。'?請參考相關(guān)博客添加相關(guān)的環(huán)境變量。參考博客。 3.3??在生成了相關(guān)的庫文件的編寫后。我們要用這個(gè)庫文件有兩種辦法。
3.4運(yùn)行代碼: public class Com {
static {
System.loadLibrary("CommDLL"); //靜態(tài)語句塊,保證在創(chuàng)建該類的時(shí)候,該方法必須且只會(huì)調(diào)用一次。
}
public native int Comm(int nBaud,int parity,int bytesize,int stopbits,int accdatalength,String ComName,byte[] rBuf);
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] Buffer = new byte[18];
while(true)
{
int term = new Com().Comm(4800, 0, 8, 0, 17,"COM3", Buffer);
if(term==1)
{
for(int i=0;i<17;i++)
{
System.out.print(Buffer[i]+" ");
}
System.out.println();
}
}
}
}運(yùn)行結(jié)果
四、C/C++封裝動(dòng)態(tài)運(yùn)行庫4.1:關(guān)于工具??一開始用的工具是VC++6.0,盡管工具有點(diǎn)老,但是它足夠小。動(dòng)態(tài)庫編好了,在eclipse上面運(yùn)行的時(shí)候,出現(xiàn)了錯(cuò)誤,大致意思就是:“32位的動(dòng)態(tài)庫,沒有辦法在64位的設(shè)備上面”。 如果,我還想繼續(xù)使用這個(gè)動(dòng)態(tài)庫,已知的解決辦法就是:下載一個(gè)32位的java ? JDK包。但是,相比配置一個(gè)可以運(yùn)行32位的環(huán)境,我更加傾向于編譯生成一個(gè)64位的動(dòng)態(tài)運(yùn)行庫。然后,我就把代碼粘到了VS上面。一般寫代碼我不太喜歡在VS上面,雖然它的功能很強(qiáng)太,但是我的電腦負(fù)載它真的很費(fèi)力。 4.2 用JNI編寫本地函具體步驟?? * 1:建立DLL程序:文件--->新建--->項(xiàng)目--->windows桌面--->動(dòng)態(tài)鏈接庫
4.2 代碼細(xì)節(jié)?? * 1:新建一個(gè)頭文件,把在第三節(jié)里面生成的頭文件內(nèi)容復(fù)制粘貼過來。 #include <iostream>
#include<stdlib.h>
#include<windows.h>
using std::string;
int Comm(int nBaud, int parity, int bytesize, int stopbits, int accdatalength,string comName, char rBuf[])
{
DWORD rSize = 0;
DWORD dwError; //清除錯(cuò)誤
COMSTAT cs;
COMMTIMEOUTS timeouts; //超時(shí)數(shù)據(jù)結(jié)構(gòu)
DCB dcb; //串口通信配置文件---用LPDCB類型會(huì)報(bào)錯(cuò)
HANDLE hCom; //串口的句柄(實(shí)例)| the instance of com
//////////////////////////////////////////////////////////////////////
//將string轉(zhuǎn)換為LPCWSTR類型
//////////////////////////////////////////////////////////////////////
int len = comName.length();
WCHAR buffer[256];
///////memset原型---extern void *memset(void *buffer, int c, int count) buffer:為指針或是數(shù)組,c:是賦給buffer的值,count:是buffer的長度.//////
memset(buffer, 0, sizeof(buffer)); //作用是在一段內(nèi)存塊中填充某個(gè)給定的值,它是對較大的結(jié)構(gòu)體或數(shù)組進(jìn)行清零操作的一種最快方法
MultiByteToWideChar(CP_ACP, 0, comName.c_str(), (len+1), buffer, sizeof(buffer) / sizeof(buffer[0]));
printf("%d\n", buffer[0]);
hCom = CreateFile(buffer, //串口的名字
GENERIC_READ | GENERIC_WRITE, //串口打開方式
0, //共享方式
NULL, //安全屬性
OPEN_EXISTING, //指定文件的動(dòng)作
0, //文件屬性---不指定就默認(rèn)為同步IO
NULL //指向模板文件的句柄
);
if (hCom == INVALID_HANDLE_VALUE)
{
return -1;
}
///////////////////////////////////////
//同步IO需要設(shè)置讀數(shù)據(jù)超時(shí)
//////////////////////////////////////
if (!(GetCommTimeouts(hCom, &timeouts))) //獲的COMMTIMEOUTS結(jié)構(gòu)失敗!
{
CloseHandle(hCom);
}
timeouts.ReadIntervalTimeout = 1000; //讀取每個(gè)字符之間的超時(shí)
timeouts.ReadTotalTimeoutMultiplier = 500; //讀取一個(gè)字符的超時(shí)
timeouts.ReadTotalTimeoutConstant = 5000; //固定總超時(shí)
timeouts.WriteTotalTimeoutConstant = 0; //寫入字符之間超時(shí)
timeouts.WriteTotalTimeoutMultiplier = 0; //寫入字符總超時(shí)
if (!(SetCommTimeouts(hCom, &timeouts))) //設(shè)置COMMTIMEOUTS結(jié)構(gòu)失敗
{
CloseHandle(hCom);
}
////////////////////////////////////////
//設(shè)置緩沖區(qū)大小
///////////////////////////////////////
if (!SetupComm(hCom, 500, 500)) //設(shè)置讀寫緩沖區(qū)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//設(shè)置波特率等其它讀文件配置
/////////////////////////////////////
if (GetCommState(hCom, &dcb) == 0) //獲得DCB數(shù)據(jù)失敗
{
CloseHandle(hCom);
}
//dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = nBaud; //波特率為4800
dcb.Parity = parity; //校驗(yàn)方式為無校驗(yàn)
dcb.ByteSize = bytesize; //數(shù)據(jù)位為8位
dcb.StopBits = stopbits; //停止位為1位
if (!SetCommState(hCom, &dcb)) //設(shè)置串口通信配置項(xiàng)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//清除緩沖區(qū)
/////////////////////////////////////
PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR);
///////////////////////////////////////
//清除錯(cuò)誤
///////////////////////////////////////
if (!(ClearCommError(hCom, &dwError, &cs)))
{
CloseHandle(hCom);
}
////////////////////////////////////////////
//開始讀取緩沖口的數(shù)據(jù)
///////////////////////////////////////////
//讀取串口數(shù)據(jù)
if (INVALID_HANDLE_VALUE != hCom)
{
ReadFile(hCom, rBuf, accdatalength, &rSize, NULL);
printf("%d \n", rSize);
if (rSize)
{
CloseHandle(hCom);
return 1;
}
else
{
CloseHandle(hCom);
return 0;
}
}
else
{
CloseHandle(hCom);
return 2;
}
}
void main()
{
int i;
char buf[18] = { 0 };
int a, b, c, d, e;
char ComName[6];
printf("請輸入相關(guān)參數(shù):");
std::cin >> a >> b >> c >> d >> e>>ComName;
while (1)
{
int t = Comm(a, b, c, d, e, ComName, buf);
if (t == 1)
{
for (i = 0; i < 17; i++)
{
printf("%c ", buf[i]);
}
printf("\n");
}
else
{
printf("%d\n", t);
break;
}
}
}
#include "pch.h"
#include "comm.h"
#include <iostream>
#include <stdlib.h>
#include <windows.h>
using std::string;
/////////////////////////////////////////////////////////////////////////
//
JNIEXPORT jint JNICALL Java_Com_Comm(JNIEnv* env, jobject, jint nBaud, jint parity, jint bytesize, jint stopbits, jint accdatalength,jstring comName, jbyteArray rBuf)
{ DWORD rSize = 0;
DWORD dwError; //清除錯(cuò)誤
COMSTAT cs;
COMMTIMEOUTS timeouts; //超時(shí)數(shù)據(jù)結(jié)構(gòu)
DCB dcb; //串口通信配置文件---用LPDCB類型會(huì)報(bào)錯(cuò)
HANDLE hCom; //串口的句柄(實(shí)例)| the instance of com
BYTE rBuffer[255];
//////////////////////////////////////////////////////////////////////
//將jstring轉(zhuǎn)換為LPCWSTR類型
//////////////////////////////////////////////////////////////////////
WCHAR* buffer = (WCHAR*)env->GetStringChars(comName,NULL);
hCom = CreateFile(buffer, //串口的名字
GENERIC_READ | GENERIC_WRITE, //串口打開方式
0, //共享方式
NULL, //安全屬性
OPEN_EXISTING, //指定文件的動(dòng)作
0, //文件屬性---不指定就默認(rèn)為同步IO
NULL //指向模板文件的句柄
);
if (hCom == INVALID_HANDLE_VALUE)
{
return -1;
}
///////////////////////////////////////
//同步IO需要設(shè)置讀數(shù)據(jù)超時(shí)
//////////////////////////////////////
if (!(GetCommTimeouts(hCom, &timeouts))) //獲的COMMTIMEOUTS結(jié)構(gòu)失?。? {
CloseHandle(hCom);
}
timeouts.ReadIntervalTimeout = 1000; //讀取每個(gè)字符之間的超時(shí)
timeouts.ReadTotalTimeoutMultiplier = 500; //讀取一個(gè)字符的超時(shí)
timeouts.ReadTotalTimeoutConstant = 5000; //固定總超時(shí)
timeouts.WriteTotalTimeoutConstant = 0; //寫入字符之間超時(shí)
timeouts.WriteTotalTimeoutMultiplier = 0; //寫入字符總超時(shí)
if (!(SetCommTimeouts(hCom, &timeouts))) //設(shè)置COMMTIMEOUTS結(jié)構(gòu)失敗
{
CloseHandle(hCom);
}
////////////////////////////////////////
//設(shè)置緩沖區(qū)大小
///////////////////////////////////////
if (!SetupComm(hCom, 500, 500)) //設(shè)置讀寫緩沖區(qū)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//設(shè)置波特率等其它讀文件配置
/////////////////////////////////////
if (GetCommState(hCom, &dcb) == 0) //獲得DCB數(shù)據(jù)失敗
{
CloseHandle(hCom);
}
//dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = nBaud; //波特率為4800
dcb.Parity = parity; //校驗(yàn)方式為無校驗(yàn)
dcb.ByteSize = bytesize; //數(shù)據(jù)位為8位
dcb.StopBits = stopbits; //停止位為1位
if (!SetCommState(hCom, &dcb)) //設(shè)置串口通信配置項(xiàng)失敗
{
CloseHandle(hCom);
}
//////////////////////////////////////
//清除緩沖區(qū)
/////////////////////////////////////
PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR);
///////////////////////////////////////
//清除錯(cuò)誤
///////////////////////////////////////
if (!(ClearCommError(hCom, &dwError, &cs)))
{
CloseHandle(hCom);
}
////////////////////////////////////////////
//開始讀取緩沖口的數(shù)據(jù)
///////////////////////////////////////////
//讀取串口數(shù)據(jù)
if (INVALID_HANDLE_VALUE != hCom)
{
ReadFile(hCom, &rBuffer, accdatalength, &rSize, NULL);
if (rSize)
{
jbyte* term; //將RBuffer中的值賦給rBuf
term = env->GetByteArrayElements(rBuf,FALSE);
for (int i = 0; i < accdatalength; i++)
{
term[i] = rBuffer[i];
}
env->ReleaseByteArrayElements(rBuf,term, JNI_COMMIT);
CloseHandle(hCom);
return 1;
}
}
else
{
CloseHandle(hCom);
return 1;
}
env->ReleaseStringChars(comName,(jchar *)buffer); //釋放空間
CloseHandle(hCom);
return 2;
}注:代碼寫在自己新建的和頭文件同名的自己建立的源文件(.cpp)里面。 4.3 配置在生成DLL文件之前,我們還必須做一些重要的配置。
??將框起來的三個(gè)頭文件復(fù)制到DLL源程序的文件里面。
右鍵項(xiàng)目 ---> 屬性--->VC++目錄--->包含目錄(把DLL程序所在的文件路徑添加進(jìn)去) |
|
|