当前位置:   article > 正文

arm嵌入式系统下用golang读取扫码枪数据_golang 扫码枪

golang 扫码枪

项目背景:

    成人本科的论文选题是用golang做一个简易的嵌入式POS机应用, 支持扫zfb/wx的在线支付二维码, 所以用c封装了几个函数给golang使用. 那这里面又涉及到了另一个问题, 如何使用arm版golang.

    在我前面的文章里有一篇如何去编译arm版golang, 但是就这个项目而言, 我忽略了一个问题: golang调用c代码的时候, 需要指定gcc, 而我所指定的gcc是amd64架构, 就算直接copy到arm板子上也不能用, 还需要编译arm版gcc, 这就很麻烦. 还好, 我们同样可以通过指定交叉编译器去交叉编译golang的代码, 这只需要通过一些简单的设置就能成功. 

环境说明:

    执行环境: Linux ubuntu 5.0.0-32-generic. 即我编译(执行go build)golang代码的环境.

    目标环境: arm.

设置参数:

    首先, 在编译之前, 要确保ubuntu下有golang环境(能执行go命令), 我的golang版本是:

go version go1.13.10 linux/amd64

    golang的环境变量设置跟我前面如何编译arm版golang文章里设置的环境变量差不多, 如下:

export GO111MODULE=on
export GOPROXY=https://goproxy.io
export GOARM=7
export GOARCH=arm
export GOOS=linux
export CGO_ENABLED=1
export CC_FOR_TARGET=/opt/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin/arm-linux-gnueabihf-gcc
export CXX_FOR_TARGET=/opt/gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux/bin/arm-linux-gnueabihf-g++
export GOROOT=/usr/local/go
export GOPATH=/usr/local/gopath

    主要是开启cgo和指定交叉编译器.

代码展示:

    首先, 目录结构如下:

    

    其中目录cdep/包含了scanDevice.h/scanDevice.c文件, 主要是指定波特率/数据位等参数, scandevice.go对外提供了一个包. 

    scanDevice.h代码如下:

  1. #ifndef SCANDEVICE_H
  2. #define SCANDEVICE_H
  3. #include <sys/types.h>
  4. #include <sys/syscall.h>
  5. #include <sys/ioctl.h>
  6. #include <termios.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <stdbool.h>
  10. #define MSG_SIZE_MIN 20
  11. #define MSG_SIZE_MAX 1024
  12. typedef struct SerialInfo
  13. {
  14. unsigned char databit;
  15. unsigned char stopbit;
  16. speed_t rate;
  17. }SerialInfo;
  18. int32_t SetGetFD(int32_t rate, const char *file);
  19. bool Read(int32_t fd, void *msg, int32_t msgSize);
  20. void CloseFD(int32_t fd);
  21. int32_t openSerail(const char *file);
  22. int32_t setTermios(int32_t fd, const SerialInfo *serialInfo);
  23. speed_t getBaudRate(const int32_t rate);
  24. void setDataBit(unsigned char bit, struct termios *setting);
  25. void setParity( unsigned char *str, struct termios *setting);
  26. void setStopBit(unsigned char bit, struct termios *setting);
  27. #endif

    scanDevice.c代码如下:

  1. #include <unistd.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include "scanDevice.h"
  5. // 设置获取FD
  6. int32_t SetGetFD(int32_t rate, const char *file)
  7. {
  8. int32_t fd = 0;
  9. if ((fd = openSerail(file)) == -1)
  10. {
  11. return -1;
  12. }
  13. SerialInfo serialInfo = {'8', '1', getBaudRate(rate)};
  14. setTermios(fd, (const SerialInfo *)&serialInfo);
  15. return fd;
  16. }
  17. //FD读数据
  18. bool Read(int32_t fd, void *msg, int32_t msgSize)
  19. {
  20. if (msgSize < MSG_SIZE_MIN || msgSize > MSG_SIZE_MAX)
  21. {
  22. return false;
  23. }
  24. int32_t offset = 0;
  25. while(true)
  26. {
  27. int32_t ret = read(fd, (unsigned char *)msg + offset, msgSize);
  28. if (ret == -1) // 读取失败, 比如关闭FD
  29. {
  30. return false;
  31. }
  32. if (strstr((unsigned char *)msg, "\r\n") != NULL) // 此扫码枪会在数据的结尾增加\r\n, 我以此为判断条件
  33. {
  34. return true;
  35. }
  36. offset += ret;
  37. }
  38. }
  39. // 关闭FD
  40. void CloseFD(int32_t fd)
  41. {
  42. close(fd);
  43. }
  44. int32_t openSerail(const char *file)
  45. {
  46. return open(file, O_RDWR|O_NOCTTY);
  47. }
  48. int32_t setTermios(int32_t fd, const SerialInfo *serialInfo)
  49. {
  50. struct termios setting;
  51. tcgetattr(fd, &setting);
  52. //设置波特率
  53. cfsetispeed(&setting, serialInfo->rate);
  54. cfsetospeed(&setting, serialInfo->rate);
  55. cfmakeraw(&setting);
  56. setDataBit(serialInfo->databit, &setting);
  57. setParity("none", &setting);
  58. setStopBit(serialInfo->stopbit, &setting);
  59. tcflush(fd, TCIFLUSH);
  60. setting.c_cc[VTIME] = 0;
  61. setting.c_cc[VMIN] = 1;
  62. tcsetattr(fd, TCSANOW, &setting);
  63. return 0;
  64. }
  65. // getBaudRate 获取波特率
  66. speed_t getBaudRate(const int32_t rate)
  67. {
  68. switch (rate)
  69. {
  70. case 4800:
  71. return B4800;
  72. case 9600:
  73. return B9600;
  74. case 19200:
  75. return B19200;
  76. case 38400:
  77. return B38400;
  78. case 57600:
  79. return B57600;
  80. case 115200:
  81. return B115200;
  82. default:
  83. return B115200;
  84. }
  85. }
  86. // setDataBit 设置数据位
  87. void setDataBit(unsigned char bit, struct termios *setting)
  88. {
  89. switch (bit)
  90. {
  91. case '8':
  92. setting->c_cflag |= CS8;
  93. break;
  94. case '7':
  95. setting->c_cflag |= CS7;
  96. break;
  97. case '6':
  98. setting->c_cflag |= CS6;
  99. break;
  100. case '5':
  101. setting->c_cflag |= CS5;
  102. break;
  103. default:
  104. setting->c_cflag |= CS8;
  105. break;
  106. }
  107. }
  108. // setParity 设置parity
  109. void setParity( unsigned char *str, struct termios *setting)
  110. {
  111. if (strcmp("odd",(char *)str) == 0)
  112. {
  113. setting->c_cflag |= (PARODD | PARENB);
  114. setting->c_iflag |= INPCK;
  115. }
  116. else if (strcmp("even",(char *)str) == 0)
  117. {
  118. setting->c_cflag |= PARENB;
  119. setting->c_cflag &= ~PARODD;
  120. setting->c_iflag |= INPCK;
  121. }
  122. else // 默认, 可以填写str为"none"
  123. {
  124. setting->c_cflag &= ~PARENB;
  125. setting->c_iflag &= ~INPCK;
  126. }
  127. }
  128. // setStopBit 设置停止位
  129. void setStopBit(unsigned char bit, struct termios *setting)
  130. {
  131. switch (bit)
  132. {
  133. case '1':
  134. setting->c_cflag &= ~CSTOPB;
  135. break;
  136. case '2':
  137. setting->c_cflag |= CSTOPB;
  138. break;
  139. default:
  140. setting->c_cflag &= ~CSTOPB;
  141. break;
  142. }
  143. }

    scandevice.go代码如下:

  1. package cdep
  2. /*
  3. #include <stdlib.h>
  4. #include "scanDevice.h"
  5. #cgo CFLAGS: -I./
  6. */
  7. import "C"
  8. import (
  9. "reflect"
  10. "unsafe"
  11. )
  12. // SetGetFD ..
  13. func SetGetFD(rate int32, file string) int32 {
  14. filePtr := C.CString(file)
  15. defer C.free(unsafe.Pointer(filePtr))
  16. return int32(C.SetGetFD(C.int(rate), filePtr))
  17. }
  18. // ReadFD ..
  19. func ReadFD(fd int32, msg []byte, msgSize int32) bool {
  20. return bool(C.Read(C.int(fd), unsafe.Pointer(reflect.ValueOf(msg).Pointer()), C.int(msgSize)))
  21. }
  22. // CloseFD ..
  23. func CloseFD(fd int32) {
  24. C.CloseFD(C.int(fd))
  25. }

    这部分golang代码, 有一点需要注意: 在调用C.CString()函数的时候, 会调用c的malloc函数, 记得用C.free释放.

    scan.go代码主要是调用cdep的包, 如下:

  1. package armscan
  2. import (
  3. "log"
  4. "armscan/cdep"
  5. )
  6. // MacroMsgSize 宏定义
  7. const MacroMsgSize = 1024
  8. // Scan ..
  9. func Scan() {
  10. file := "/dev/ttymxc6"
  11. fd := cdep.SetGetFD(9600, file)
  12. if fd == -1 {
  13. log.Println("SetGetFD failed.")
  14. return
  15. }
  16. log.Println("SetGetFD success.")
  17. defer cdep.CloseFD(fd)
  18. for {
  19. var msg = make([]byte, MacroMsgSize)
  20. if !cdep.ReadFD(fd, msg, int32(MacroMsgSize)) {
  21. log.Fatal("err")
  22. }
  23. log.Println(string(msg))
  24. }
  25. }

    main.go就一行代码, 调用包armscan里的Scan()函数, 这里不再展示.

测试展示:

    在支付宝上打开了KFC会员卡, 扫码结果跟实际的卡号一致, 如下:

结束.

    c代码里的read函数可能需要根据你的需求来更改, 让它更趋于完善.

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/974549
推荐阅读
  

闽ICP备14008679号