【imx6ul】linux下rs485的使用

0、说明

        对于linux下的485使用,其实就是linux下的串口使用。但是485有一个控制信号,在485等待接收的时候,控制信号需要时低电平,在发送的时候需要为高电平。所以对于linux下485驱动,最主要的任务就是完的成对控制信号电平的操作

        该控制信号就是485芯片的2/3号引脚。

 1、485测试程序及改进过程

       如下demo程序,其实大部分代码是一个串口的程序。主要关注一下如下的rs485_enable函数。该函数通过ioctl的方式配置了使能参数。

 
  1. #include <stdio.h>

  2. #include <termios.h>

  3. #include <linux/ioctl.h>

  4. #include <linux/serial.h>

  5. #include <asm-generic/ioctls.h> /* TIOCGRS485 + TIOCSRS485 ioctl definitions */

  6. #include <unistd.h>

  7. #include <errno.h>

  8. #include <fcntl.h>

  9. #include <sys/types.h>

  10. #include <sys/stat.h>

  11. #include <string.h>

  12. #include <stdlib.h>

  13. #include <getopt.h>

  14. /**

  15. * @brief: set the properties of serial port

  16. * @Param: fd: file descriptor

  17. * @Param: nSpeed: Baud Rate

  18. * @Param: nBits: character size

  19. * @Param: nEvent: parity of serial port

  20. * @Param: nStop: stop bits

  21. */

  22. typedef enum {DISABLE = 0, ENABLE} RS485_ENABLE_t;

  23. int set_port(int fd, int nSpeed, int nBits, char nEvent, int nStop)

  24. {

  25. struct termios newtio, oldtio;

  26. memset(&oldtio, 0, sizeof(oldtio));

  27. /* save the old serial port configuration */

  28. if(tcgetattr(fd, &oldtio) != 0) {

  29. perror("set_port/tcgetattr");

  30. return -1;

  31. }

  32. memset(&newtio, 0, sizeof(newtio));

  33. /* ignore modem control lines and enable receiver */

  34. newtio.c_cflag = newtio.c_cflag |= CLOCAL | CREAD;

  35. newtio.c_cflag &= ~CSIZE;

  36. /* set character size */

  37. switch (nBits) {

  38. case 8:

  39. newtio.c_cflag |= CS8;

  40. break;

  41. case 7:

  42. newtio.c_cflag |= CS7;

  43. break;

  44. case 6:

  45. newtio.c_cflag |= CS6;

  46. break;

  47. case 5:

  48. newtio.c_cflag |= CS5;

  49. break;

  50. default:

  51. newtio.c_cflag |= CS8;

  52. break;

  53. }

  54. /* set the parity */

  55. switch (nEvent) {

  56. case 'o':

  57. case 'O':

  58. newtio.c_cflag |= PARENB;

  59. newtio.c_cflag |= PARODD;

  60. newtio.c_iflag |= (INPCK | ISTRIP);

  61. break;

  62. case 'e':

  63. case 'E':

  64. newtio.c_cflag |= PARENB;

  65. newtio.c_cflag &= ~PARODD;

  66. newtio.c_iflag |= (INPCK | ISTRIP);

  67. break;

  68. case 'n':

  69. case 'N':

  70. newtio.c_cflag &= ~PARENB;

  71. break;

  72. default:

  73. newtio.c_cflag &= ~PARENB;

  74. break;

  75. }

  76. /* set the stop bits */

  77. switch (nStop) {

  78. case 1:

  79. newtio.c_cflag &= ~CSTOPB;

  80. break;

  81. case 2:

  82. newtio.c_cflag |= CSTOPB;

  83. break;

  84. default:

  85. newtio.c_cflag &= ~CSTOPB;

  86. break;

  87. }

  88. /* set output and input baud rate */

  89. switch (nSpeed) {

  90. case 0:

  91. cfsetospeed(&newtio, B0);

  92. cfsetispeed(&newtio, B0);

  93. break;

  94. case 50:

  95. cfsetospeed(&newtio, B50);

  96. cfsetispeed(&newtio, B50);

  97. break;

  98. case 75:

  99. cfsetospeed(&newtio, B75);

  100. cfsetispeed(&newtio, B75);

  101. break;

  102. case 110:

  103. cfsetospeed(&newtio, B110);

  104. cfsetispeed(&newtio, B110);

  105. break;

  106. case 134:

  107. cfsetospeed(&newtio, B134);

  108. cfsetispeed(&newtio, B134);

  109. break;

  110. case 150:

  111. cfsetospeed(&newtio, B150);

  112. cfsetispeed(&newtio, B150);

  113. break;

  114. case 200:

  115. cfsetospeed(&newtio, B200);

  116. cfsetispeed(&newtio, B200);

  117. break;

  118. case 300:

  119. cfsetospeed(&newtio, B300);

  120. cfsetispeed(&newtio, B300);

  121. break;

  122. case 600:

  123. cfsetospeed(&newtio, B600);

  124. cfsetispeed(&newtio, B600);

  125. break;

  126. case 1200:

  127. cfsetospeed(&newtio, B1200);

  128. cfsetispeed(&newtio, B1200);

  129. break;

  130. case 1800:

  131. cfsetospeed(&newtio, B1800);

  132. cfsetispeed(&newtio, B1800);

  133. break;

  134. case 2400:

  135. cfsetospeed(&newtio, B2400);

  136. cfsetispeed(&newtio, B2400);

  137. break;

  138. case 4800:

  139. cfsetospeed(&newtio, B4800);

  140. cfsetispeed(&newtio, B4800);

  141. break;

  142. case 9600:

  143. cfsetospeed(&newtio, B9600);

  144. cfsetispeed(&newtio, B9600);

  145. break;

  146. case 19200:

  147. cfsetospeed(&newtio, B19200);

  148. cfsetispeed(&newtio, B19200);

  149. break;

  150. case 38400:

  151. cfsetospeed(&newtio, B38400);

  152. cfsetispeed(&newtio, B38400);

  153. break;

  154. case 57600:

  155. cfsetospeed(&newtio, B57600);

  156. cfsetispeed(&newtio, B57600);

  157. break;

  158. case 115200:

  159. cfsetospeed(&newtio, B115200);

  160. cfsetispeed(&newtio, B115200);

  161. break;

  162. case 230400:

  163. cfsetospeed(&newtio, B230400);

  164. cfsetispeed(&newtio, B230400);

  165. break;

  166. default:

  167. cfsetospeed(&newtio, B115200);

  168. cfsetispeed(&newtio, B115200);

  169. break;

  170. }

  171. /* set timeout in deciseconds for non-canonical read */

  172. newtio.c_cc[VTIME] = 0;

  173. /* set minimum number of characters for non-canonical read */

  174. newtio.c_cc[VMIN] = 0;

  175. /* flushes data received but not read */

  176. tcflush(fd, TCIFLUSH);

  177. /* set the parameters associated with the terminal from

  178. the termios structure and the change occurs immediately */

  179. if((tcsetattr(fd, TCSANOW, &newtio))!=0)

  180. {

  181. perror("set_port/tcsetattr");

  182. return -1;

  183. }

  184. return 0;

  185. }

  186. /**

  187. * @brief: open serial port

  188. * @Param: dir: serial device path

  189. */

  190. int open_port(char *dir)

  191. {

  192. int fd ;

  193. fd = open(dir, O_RDWR);

  194. if(fd < 0) {

  195. perror("open_port");

  196. }

  197. return fd;

  198. }

  199. /**

  200. * @brief: print usage message

  201. * @Param: stream: output device

  202. * @Param: exit_code: error code which want to exit

  203. */

  204. void print_usage (FILE *stream, int exit_code)

  205. {

  206. fprintf(stream, "Usage: option [ dev... ] \n");

  207. fprintf(stream,

  208. "\t-h --help Display this usage information.\n"

  209. "\t-d --device The device ttyS[0-3] or ttySCMA[0-1]\n"

  210. "\t-b --baudrate Set the baud rate you can select\n"

  211. "\t [230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300]\n"

  212. "\t-s --string Write the device data\n"

  213. "\t-e --1 or 0 , Write 1 to enable rs485_mode(only at atmel)\n");

  214. exit(exit_code);

  215. }

  216. /**

  217. * @brief: main function

  218. * @Param: argc: number of parameters

  219. * @Param: argv: parameters list

  220. */

  221. int rs485_enable(const int fd, const RS485_ENABLE_t enable)

  222. {

  223. struct serial_rs485 rs485conf;

  224. int res;

  225. /* Get configure from device */

  226. res = ioctl(fd, TIOCGRS485, &rs485conf);

  227. if (res < 0) {

  228. perror("Ioctl error on getting 485 configure:");

  229. close(fd);

  230. return res;

  231. }

  232. /* Set enable/disable to configure */

  233. if (enable) { // Enable rs485 mode

  234. rs485conf.flags |= SER_RS485_ENABLED;

  235. } else { // Disable rs485 mode

  236. rs485conf.flags &= ~(SER_RS485_ENABLED);

  237. }

  238. rs485conf.delay_rts_before_send = 0x00000004;

  239. /* Set configure to device */

  240. res = ioctl(fd, TIOCSRS485, &rs485conf);

  241. if (res < 0) {

  242. perror("Ioctl error on setting 485 configure:");

  243. close(fd);

  244. }

  245. return res;

  246. }

  247. int main(int argc, char *argv[])

  248. {

  249. char *write_buf = "0123456789";

  250. char read_buf[100];

  251. int fd, i, len, nread,r;

  252. pid_t pid;

  253. int next_option;

  254. extern struct termios oldtio;

  255. int speed ;

  256. char *device;

  257. int spee_flag = 0, device_flag = 0;

  258. const char *const short_options = "hd:s:b:e:";

  259. const struct option long_options[] = {

  260. { "help", 0, NULL, 'h'},

  261. { "device", 1, NULL, 'd'},

  262. { "string", 1, NULL, 's'},

  263. { "baudrate", 1, NULL, 'b'},

  264. { NULL, 0, NULL, 0 }

  265. };

  266. if (argc < 2) {

  267. print_usage (stdout, 0);

  268. exit(0);

  269. }

  270. while (1) {

  271. next_option = getopt_long (argc, argv, short_options, long_options, NULL);

  272. if (next_option < 0)

  273. break;

  274. switch (next_option) {

  275. case 'h':

  276. print_usage (stdout, 0);

  277. break;

  278. case 'd':

  279. device = optarg;

  280. device_flag = 1;

  281. break;

  282. case 'b':

  283. speed = atoi(optarg);

  284. spee_flag = 1;

  285. break;

  286. case 's':

  287. write_buf = optarg;

  288. break;

  289. case 'e':

  290. r = atoi(optarg);

  291. break;

  292. case '?':

  293. print_usage (stderr, 1);

  294. break;

  295. default:

  296. abort ();

  297. }

  298. }

  299. if ((!device_flag)||(!spee_flag)) {

  300. print_usage (stderr, 1);

  301. exit(0);

  302. }

  303. /* open serial port */

  304. fd = open_port(device);

  305. if (fd < 0) {

  306. perror("open failed");

  307. return -1;

  308. }

  309. if(r)

  310. {

  311. rs485_enable(fd,ENABLE);

  312. }

  313. /* set serial port */

  314. i = set_port(fd, speed, 8, 'N', 1);

  315. if (i < 0) {

  316. perror("set_port failed");

  317. return -1;

  318. }

  319. while (1) {

  320. /* if new data is available on the serial port, read and print it out */

  321. nread = read(fd ,read_buf ,sizeof(read_buf));

  322. if (nread > 0) {

  323. printf("RECV[%3d]: ", nread);

  324. for(i = 0; i < nread; i++)

  325. printf("0x%02x ", read_buf[i]);

  326. printf("\n");

  327. write(fd, read_buf, nread);//自己添加

  328. }

  329. }

  330. /* restore the old configuration */

  331. tcsetattr(fd, TCSANOW, &oldtio);

  332. close(fd);

  333. return 0;

  334. }

        执行以上app,会发现,app接收到第一帧数据后,可以发送回去,之后外接再来数据的时候已经接收不上来了。测试DE控制信号,发现是高,也就是发送完成没有将DE拉低。

        当然这个程序本是开发板厂家提供用于硬件测试的。厂家485测试提供了两个app一个专门用于收的测试,一个专门用于发的测试,或许就是因为提供的APP过于简单吧。

        那么为什么呢。第一个就是找出为什么发送后控制信号被拉高后没有再次拉低,导致无法接收。

        查看内核485帮助文档。似乎以上程序是确少了一些配置。如内核提示了SER_RS485_RTS等相关的config设置,这些设置应该就是会影响控制信号的控制。

 
  1. From user-level, RS485 configuration can be get/set using the previous

  2. ioctls. For instance, to set RS485 you can use the following code:

  3. #include <linux/serial.h>

  4. /* Driver-specific ioctls: */

  5. #define TIOCGRS485 0x542E

  6. #define TIOCSRS485 0x542F

  7. /* Open your specific device (e.g., /dev/mydevice): */

  8. int fd = open ("/dev/mydevice", O_RDWR);

  9. if (fd < 0) {

  10. /* Error handling. See errno. */

  11. }

  12. struct serial_rs485 rs485conf;

  13. /* Enable RS485 mode: */

  14. rs485conf.flags |= SER_RS485_ENABLED;

  15. /* Set logical level for RTS pin equal to 1 when sending: */

  16. rs485conf.flags |= SER_RS485_RTS_ON_SEND;

  17. /* or, set logical level for RTS pin equal to 0 when sending: */

  18. rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);

  19. /* Set logical level for RTS pin equal to 1 after sending: */

  20. rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;

  21. /* or, set logical level for RTS pin equal to 0 after sending: */

  22. rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);

  23. /* Set rts delay before send, if needed: */

  24. rs485conf.delay_rts_before_send = ...;

  25. /* Set rts delay after send, if needed: */

  26. rs485conf.delay_rts_after_send = ...;

  27. /* Set this flag if you want to receive data even whilst sending data */

  28. rs485conf.flags |= SER_RS485_RX_DURING_TX;

  29. if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {

  30. /* Error handling. See errno. */

  31. }

  32. /* Use read() and write() syscalls here... */

  33. /* Close the device when finished: */

  34. if (close (fd) < 0) {

  35. /* Error handling. See errno. */

  36. }

 优化1:发送完成后,控制信号始终为高

        增加了如下标志后,发现发送完成后,控制信号被拉低,可以再次接收数据了。

        rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;

 
  1. /*

  2. * interrupts disabled on entry

  3. */

  4. static void imx_stop_tx(struct uart_port *port)

  5. {

  6. struct imx_port *sport = (struct imx_port *)port;

  7. unsigned long temp;

  8. /*

  9. * We are maybe in the SMP context, so if the DMA TX thread is running

  10. * on other cpu, we have to wait for it to finish.

  11. */

  12. if (sport->dma_is_enabled && sport->dma_is_txing)

  13. return;

  14. temp = readl(port->membase + UCR1);

  15. writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);

  16. /* in rs485 mode disable transmitter if shifter is empty */

  17. if (port->rs485.flags & SER_RS485_ENABLED &&

  18. readl(port->membase + USR2) & USR2_TXDC) {

  19. if (sport->txen_gpio != -1) {

  20. if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND){

  21. gpio_set_value(sport->txen_gpio, 0);

  22. }

  23. }

可以定位到,在发送完成后,即imx_stop_tx,根据SER_RS485_RTS_AFTER_SEND设置情况,将控制信号拉低了。

优化2:上电后第二次运行APP的时候出现控制信号常态为高

        定位到原因是第一次执行app时,已经配置了SER_RS485_RTS_AFTER_SEND标志,当再次配置时,出现拉高控制信号。具体在驱动如下:

 
  1. static int imx_rs485_config(struct uart_port *port,

  2. struct serial_rs485 *rs485conf)

  3. {

  4. struct imx_port *sport = (struct imx_port *)port;

  5. /* unimplemented */

  6. rs485conf->delay_rts_before_send = 0;

  7. rs485conf->delay_rts_after_send = 0;

  8. rs485conf->flags |= SER_RS485_RX_DURING_TX;

  9. /* RTS is required to control the transmitter */

  10. if (sport->txen_gpio == -1 && !sport->have_rtscts)

  11. rs485conf->flags &= ~SER_RS485_ENABLED;

  12. if (rs485conf->flags & SER_RS485_ENABLED) {

  13. if (sport->txen_gpio != -1) {

  14. if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)

  15. gpio_set_value(sport->txen_gpio, 1);

  16. else

  17. gpio_set_value(sport->txen_gpio, 0);

  18. }

可见,当应用层执行ioctl(fd, TIOCSRS485, &rs485conf);的时候,会根据历史flag设置控制信号。因此在APP执行的时候,判断是否SER_RS485_RTS_AFTER_SEND已经被置位,则不再调用ioctl,或者先清掉历史标志,再次设置。

 (1652条消息) 【imx6ul】linux下rs485的使用_linux rs485_【星星之火】的博客-CSDN博客

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值