<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      學(xué)無止境--Linux串口編程(RS485)

      備注:學(xué)習(xí)記錄所用,若有高手不吝賜教,萬分感謝!

      一、概括

      ??linux將串口都映射成了TTY終端,所以在串口編程時(shí),找到并使能平臺的TTY,然后操作TTY終端即可。

      ??例如對于Nuclei平臺的軒轅91030M芯片設(shè)備樹:

      	uart0: serial@10013000 {
      		compatible = "sifive,uart0";
      		reg = <0x0 0x10013000 0x0 0x1000>;
      		interrupt-parent = <&plic0>;
      		interrupts = <2>;
      		clocks = <&hfclk2>;
      		status = "okay";
      	};
      
      	uart1: serial@10012000 {
      		compatible = "sifive,uart0";
      		reg = <0x0 0x10012000 0x0 0x1000>;
      		interrupt-parent = <&plic0>;
      		interrupts = <3>;
      		clocks = <&hfclk2>;
      		status = "okay";
      	};

      ??將status都設(shè)置為"okay",然后在“/dev/”目錄下就會(huì)看到ttySIF0ttySIF1兩個(gè)串口終端設(shè)備。其中ttySIF0是tty終端設(shè)備,也就是我們調(diào)試時(shí)連接到串口助手所用的串口,另一個(gè)ttySIF1就可以用來作為RS485串口。

      二、收發(fā)控制

      ??RS485是半雙工,需要一個(gè)gpio控制收發(fā)(如果硬件可以自動(dòng)收發(fā)控制,則不需要)。

      ??gpio控制收發(fā)的寫法我了解的主要有三種:

      ??1、修改tty驅(qū)動(dòng)

      ????網(wǎng)上大部分都是基于imux6ull的教程,我看到的基本都是這種,大家可以自行查找。

      ????本人認(rèn)為移植和維護(hù)比較麻煩,還要修改內(nèi)核的tty驅(qū)動(dòng)。

      ??2、用戶態(tài)驅(qū)動(dòng)

      ????以gpio5為例,就是在用戶態(tài)實(shí)現(xiàn)以下過程:

      ????echo 485 > /sys/class/gpio/export

      ????echo out > /sys/class/gpio/gpio485/direction

      ????echo 0 > /sys/class/gpio/gpio485/value 

      ????echo 1 > /sys/class/gpio/gpio485/value 

      ??3、內(nèi)核態(tài)驅(qū)動(dòng)

      ????3.1、設(shè)備樹配置

      /*
      	 * GPIO_ACTIVE_HIGH  0
      	 * GPIO_ACTIVE_LOW   1
      	 */
      	tl485ctl {
      		compatible = "tl,rs485_ctl";
      		de-gpios = <&gpio 8 1>;
      	};

      ????此配置下,若是gpio有修改,只需要修改設(shè)備樹即可。

      ????3.2、驅(qū)動(dòng)代碼

      ??????注冊一個(gè)雜項(xiàng)設(shè)備。

      查看代碼
      //#define TLMOD_DEBUG
      #ifdef TLMOD_DEBUG
      #define DPRINTK(x...) printk("tl485_ctl DEBUG:" x)
      #else
      #define DPRINTK(x...)
      #endif
      
      #define DRIVER_NAME "tl485_ctl"
      
      struct tl485_ctl{
      	int de_gpio;
      	struct device_node	*nd;  /*設(shè)備節(jié)點(diǎn)--設(shè)備樹中的 tl485ctl {...};*/
      };
      
      struct tl485_ctl tl485dev;
      
      int tl485_ctl_open(struct inode *inode,struct file *filp)
      {
      	DPRINTK("Device Opened Success!\n");
      	return nonseekable_open(inode, filp);
      }
      
      int tl485_ctl_release(struct inode *inode,struct file *filp)
      {
      	DPRINTK("Device Closed Success!\n");return 0;
      }
      
      int tl485_ctl_pm(bool enable)
      {
      	int ret = 0;
      	DPRINTK("firecxx debug: GPS PM return %d\r\n" , ret);
      	return ret;
      };
      
      long tl485_ctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
      {
      	DPRINTK("firecxx debug: tl485_ctl_ioctl cmd is %d\n" , cmd);
      	switch(cmd)
      	{       
      		case 1:
      				gpio_set_value(tl485dev.de_gpio, 1);
      				udelay(10); /*可忽略*/
      				DPRINTK("tl485_ctl Set High!\n");
      			break;
      		case 0:           
      				gpio_set_value(tl485dev.de_gpio, 0);
      				udelay(10); /*可忽略*/
      				DPRINTK("tl485_ctl Set Low!\n");
      			break;
      		default:
      			DPRINTK("tl485_ctl COMMAND ERROR!\n");
      			return -ENOTTY;
      	}
      	return 0;
      }
      
      static struct file_operations tl485_ctl_ops = {
      	.owner  = THIS_MODULE,
      	.open   = tl485_ctl_open,
      	.release= tl485_ctl_release,
      	.unlocked_ioctl     = tl485_ctl_ioctl,
      };
      
      static struct miscdevice tl485_ctl_dev = {
      	.minor  = MISC_DYNAMIC_MINOR,
      	.fops   = &tl485_ctl_ops,
      	.name   = "tl485_ctl_pin", /*真正需要操作的設(shè)備名*/
      };
      
      static int tl485_ctl_probe(struct platform_device *pdev)
      {
          int err = 0;
          int ret;
          
          DPRINTK("tl485_ctl Initialize\n");
      
      	tl485dev.nd = pdev->dev.of_node;
      	if(tl485dev.nd == NULL)
      	{
      		printk(KERN_ERR "Can't get node tl485_ctl!\n");
      		return err;
      	}
      
      	tl485dev.de_gpio = of_get_named_gpio(tl485dev.nd, "de-gpios", 0);
          if(tl485dev.de_gpio < 0)
      	{
      		printk(KERN_ERR "Can't get de_gpio!\n");
      		return err;
      	}
      
      	DPRINTK("tl485_ctl Initialize : %d\n", tl485dev.de_gpio);
      
      	err = gpio_request(tl485dev.de_gpio, "tl485_ctl");
          if (err) 
          {
              printk(KERN_ERR "failed to request GPIO_%d for ""tl485_ctl control\n", tl485dev.de_gpio);
              return err;
          }
          gpio_direction_output(tl485dev.de_gpio, 0); /*真正設(shè)置gpio方向的地方*/
          //gpio_free(tl485dev.de_gpio);
      
          ret = misc_register(&tl485_ctl_dev); /*里面實(shí)現(xiàn)了分配設(shè)備號、設(shè)備注冊、class創(chuàng)建、dev創(chuàng)建等過程*/
          if(ret<0)
          {
              printk(KERN_ERR "tl485_ctl:register device failed!\n");
              goto exit;
          }
          return 0;
      
          exit:
          misc_deregister(&tl485_ctl_dev);
      
          return ret;
      }
      
      static int tl485_ctl_remove (struct platform_device *pdev)
      {
      	gpio_free(tl485dev.de_gpio);
      	misc_deregister(&tl485_ctl_dev);  
      	return 0;
      }
      
      static int tl485_ctl_suspend (struct platform_device *pdev, pm_message_t state)
      {
      	DPRINTK("tl485_ctl suspend:power off!\n");
      	return 0;
      }
      
      static int tl485_ctl_resume (struct platform_device *pdev)
      {
      	DPRINTK("tl485_ctl resume:power on!\n");
      	return 0;
      }
      
      /* 設(shè)備樹匹配列表 */
      static const struct of_device_id tl485_ctl_of_match[] = {
      	{.compatible = "tl,rs485_ctl"},
      	{}
      };
      
      static struct platform_driver tl485_ctl_driver = {
      	.probe = tl485_ctl_probe,
      	.remove = tl485_ctl_remove,
      	.suspend = tl485_ctl_suspend,
      	.resume = tl485_ctl_resume,
      	.driver = {
      		.name = "tl485_ctl",
      		.owner = THIS_MODULE,
      		.of_match_table = tl485_ctl_of_match,
      	},
      };
      
      static int __init tl485_ctl_init(void)
      {
      	return platform_driver_register(&tl485_ctl_driver);
      }
      
      static void __exit tl485_ctl_exit(void)
      {
      	platform_driver_unregister(&tl485_ctl_driver);
      }
      
      module_init(tl485_ctl_init);
      module_exit(tl485_ctl_exit);
      MODULE_LICENSE("GPL");

      ??然后在需要時(shí)控制/dev/tl485_ctl_pin即可。

      三、用戶態(tài)代碼

      ??1、串口配置

      查看代碼
       static int io_uart_set_opt(int ttyfd,int nSpeed, int nBits, char nEvent, int nStop)
      {
          struct termios newtio, oldtio;
      	
          if (tcgetattr(ttyfd, &oldtio) != 0) 
          { 
              perror("SetupSerial 1");
      		return -1;
          }
      	
          bzero(&newtio, sizeof(newtio));
       	
          newtio.c_cflag |= CLOCAL | CREAD;
      
      	/* 設(shè)置字符大小 */
          newtio.c_cflag &= ~CSIZE;
          switch(nBits)
          {
              case 7:
      			newtio.c_cflag |= CS7;
      			break;
              case 8:
      			newtio.c_cflag |= CS8;
      			break;
          }
      
          switch(nEvent)
          {
              case 'O':
                  newtio.c_cflag |= PARENB;
      			newtio.c_cflag |= PARODD;
      			newtio.c_iflag |= (INPCK | ISTRIP);
                  break;
              case 'E': 
                  newtio.c_iflag |= (INPCK | ISTRIP);
      			newtio.c_cflag |= PARENB;
      			newtio.c_cflag &= ~PARODD;
                  break;
              case 'N':  
                  newtio.c_cflag &= ~PARENB;
                  break;
          }
      
          switch(nSpeed)
          {
              case 2400:
                  cfsetispeed(&newtio, B2400);
                  cfsetospeed(&newtio, B2400);
                  break;
              case 4800:
                  cfsetispeed(&newtio, B4800);
                  cfsetospeed(&newtio, B4800);
                  break;
              case 9600:
                  cfsetispeed(&newtio, B9600);
                  cfsetospeed(&newtio, B9600);
                  break;
              case 115200:
                  cfsetispeed(&newtio, B115200);
                  cfsetospeed(&newtio, B115200);
                  break;
              case 460800:
                  cfsetispeed(&newtio, B460800);
                  cfsetospeed(&newtio, B460800);
                  break;
              case 921600:
                  cfsetispeed(&newtio, B921600);
                  cfsetospeed(&newtio, B921600);
                  break;
              default:
                  cfsetispeed(&newtio, B9600);
      			cfsetospeed(&newtio, B9600);
                  break;
          }
      	
          if (nStop == 1)
          	newtio.c_cflag &= ~CSTOPB;
          else if (nStop == 2)
          	newtio.c_cflag |= CSTOPB;
      	
          newtio.c_cc[VTIME] = 0;
      	newtio.c_cc[VMIN] = 0;
      	
          tcflush(ttyfd, TCIOFLUSH);
      	
          if((tcsetattr(ttyfd, TCSANOW, &newtio))!=0)
          {
              perror("com set error");
              return -1;
          }
      
      	return 0;
      }

      ??需要的有不少,這些都可以在網(wǎng)上找到。

      ??2串口收發(fā)控制

      查看代碼
      static int io_rs485_to_send(void)
      {
          int ret;
      #if KERNEL_RS485_CTRL
      	int fd;
      	char *tl485_ctl = "/dev/tl485_ctl_pin";
      	
      	fd = open(tl485_ctl, O_RDWR);
      	if(fd < 0)
          {
              printf("Open %s failed\n", tl485_ctl);
              close(fd);
      		return -1;
          }
      	
          ret = ioctl(fd, 1, 0);
          if(ret<0)
          {
              printf("tl485 set ctl to high failed!\r\n");
      		close(fd);
              return -1;
          }
      
      	close(fd);
          return 0;
      #else
      	if(devGpioSet(8, 1) < 0) /* 用戶態(tài)驅(qū)動(dòng)接口 */
      		return -1;
      	
      	return 0;
      #endif
      }
      
      static int io_rs485_to_recv(void)
      {
          int ret;
      #if KERNEL_RS485_CTRL
      	int fd;
      	char *tl485_ctl = "/dev/tl485_ctl_pin";
      	
      	fd=open(tl485_ctl, O_RDWR);
      	if(fd < 0)
          {
              printf("Open %s failed\n", tl485_ctl);
              close(fd);
      		exit(1);
          }
      	
          ret = ioctl(fd, 0, 0);
          if(ret<0)
          {
          	close(fd);
              printf("tl485 set ctl to low failed!\r\n");
              return -1;
          }
      	
          close(fd);
          return 0;
      #else
      	if(devGpioSet(8, 0) < 0) /* 用戶態(tài)驅(qū)動(dòng)接口 */
      		return -1;
      
      	return 0;
      #endif
      }

      ??3、串口初始化

      查看代碼
      int devRS485InitPort(int com)
      {
      	int fd;
      	int rv;
      	int flags = 0;
      	
          /*USART_RS485_DEV就是 /dev/ 下的tty設(shè)備,如本例中就是"/dev/ttySIF1" */
      	fd = open(USART_RS485_DEV, O_RDWR | O_NOCTTY | O_NDELAY);
          if (fd < 0) {
              perror("Open error:\n");
      		exit(1);
          }
      
      	/* 恢復(fù)串口為阻塞狀態(tài) */
      	if (fcntl(fd, F_SETFL, 0) < 0) {
      		printf("fcntl failed.\n");
      		return -1;
      	}
      
          /* 測試該設(shè)備是否為tty設(shè)備 */
      	if (isatty(fd) == 0) {
      		printf("not tty device.\n");
      		return -1;
      	}
      
          rv = io_uart_set_opt(fd, 9600, 8, 'N', 1);
          if (rv < 0) {
              printf("Set uart faild\n");
      		return -1;
          }
      
      	return fd;
      }

      ??4、串口發(fā)送函數(shù)

      查看代碼
      STATUS devRS485SendDatas(int ttyfd, const char *buf, int len)
      {
      	int rv = 0;
      	ssize_t wlen = 0;
      	int sendflag = 0;
      	
      	rv = io_rs485_to_send();
      	if(rv < 0)
      	{
      		printf("set 485 to send failed\n");
      		return -1;
      	}
      
      	wlen = write(ttyfd, buf, len);
      	if (wlen != len) {
              tcflush(ttyfd, TCOFLUSH);
      		printf("write 485 failed\n");
              return -1;
          }
          
          /* write只是將數(shù)據(jù)從文件寫到了發(fā)送緩存區(qū),
           * tcdrain是等待發(fā)送緩存區(qū)發(fā)送完成,完成之前阻塞。
           * 從很多教程都說這個(gè)函數(shù)會(huì)在這里阻塞,但是我測試波形時(shí)發(fā)現(xiàn):
             收發(fā)控制管腳總是在RS485數(shù)據(jù)線剛出波形就置為接收狀態(tài)了
             !!!!!!!這里沒搞懂,所以在后面加了延時(shí)。
           */
      	rv = tcdrain(ttyfd); 
      	if(rv < 0)
      	{
      		printf("Wite 485 send failed\n");
      		return -1;
      	}
      
      	usleep(len * 1000); /*9600發(fā)送1BYTE數(shù)據(jù)大約1ms*/
      
      	rv = io_rs485_to_recv();
      	if(rv < 0)
      	{
      		printf("Set 485 recv failed\n");
      		return -1;
      	}
      
      	return 0;
      }

      ??5、接收函數(shù)

      查看代碼
      int g485fd;
      
      int open485(void)
      {
          int retval;
          fd_set rfds;
          struct timeval tv;
          int nread;
          char gRcvBuf[256];
          int gRcvLen = 0;
          
          g485fd = devRS485InitPort(0);
          if (g485fd < 0) {
              printf("error: open console error.\r\n");
              close(g485fd);
              return ERROR;
          }
          
          // wait 2.5s ,select最后一個(gè)參數(shù)
          tv.tv_sec = 2;      //阻塞時(shí)間(秒)
          tv.tv_usec = 500;   //阻塞時(shí)間(毫秒)
      	
          while (1)
          {	
              FD_ZERO(&rfds);
              FD_SET(g485fd, &rfds);
      
              retval = select(g485fd + 1 , &rfds, NULL, NULL, NULL); /*最后一個(gè)參數(shù)為NULL表示有數(shù)據(jù)前一直阻塞*/
              if (retval == -1) {
                  perror("select()");
                  break;
              }
              else if (retval) { // pan duan shi fou hai you shu ju
                  if(!FD_ISSET(g485fd,&rfds)) /*判斷是不是這個(gè)串口觸發(fā)的*/
                      continue;
                  
                  /*測試時(shí)發(fā)現(xiàn)這里每次調(diào)用read()只收到一個(gè)byte*/
                  nread = read(g485fd, gRcvBuf + gRcvLen, 256); 
                  gRcvLen += nread;
      
                  /*緩存區(qū)越界處理,這里只是隨便寫的,需要修改*/
                  if (gRcvLen >= sizeof(gRcvBuf))
                      gRcvLen = 0; 
      			
                  //printf("gRcvLen = %d ", gRcvLen);
      	
                  if (gRcvBuf[gRcvLen - 2] == '\r' && gRcvBuf[gRcvLen-1] == '\n') {
                      FD_ZERO(&rfds);
                      FD_SET(g485fd, &rfds);
      	
                      retval = select(g485fd + 1 , &rfds, NULL, NULL, NULL);
                      if (!retval) continue;// no datas, break
      			}
                  
                  /*
                   *包解析流程
                   ......
                   */
      
                  //for (int i = 0; i < 19; i++) {
                  //	printf("%02x ", gRcvBuf[i]); 
                  //}
      
                  //printf("gRcvLen=%d\r\n", gRcvLen);
              }
              else {
      			continue;
              };
          }

      ??創(chuàng)建一個(gè)任務(wù)處理串口的接收即可。

       

      參考:

      ??https://blog.csdn.net/weixin_45003868/article/details/130263090

      ??https://zhuanlan.zhihu.com/p/521283753?utm_id=0&wd=&eqid=b51aeee60009016800000003647efd5b

      posted @ 2023-11-14 12:49  xMofang  閱讀(3002)  評論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 国产精品欧美福利久久| 午夜在线观看成人av| 日韩在线观看中文字幕| 亚洲男女羞羞无遮挡久久丫| 欧美大肥婆大肥bbbbb| 亚洲激情视频一区二区三区| 亚洲国产成人无码网站大全| 亚洲乱熟乱熟女一区二区| 五月天国产成人av免费观看| 国产999久久高清免费观看| 亚洲区欧美区综合区自拍区| 国产线播放免费人成视频播放| 国产无遮挡又黄又爽免费网站| 日韩熟女熟妇久久精品综合| 无码日韩做暖暖大全免费不卡| 精品免费国产一区二区三区四区| 国产精品自拍视频我看看| 国产成人亚洲精品日韩激情| 国产精品久久一区二区三区| 国产在线观看免费观看| 欧洲精品亚洲精品日韩专区| 欧美激情 亚洲 在线| 日韩精品一区二区蜜臀av| 国产亚洲综合区成人国产| 国产一区二区内射最近更新| 欧美黑吊大战白妞| 成人国产精品三上悠亚久久| 久久中文字幕无码一区二区| 国产综合久久久久久鬼色| 中文国产成人精品久久一| 亚洲精品美女久久久久9999 | 国产精品久久亚洲不卡| 2019国产精品青青草原| 日本人妻巨大乳挤奶水免费| 日本一区二区三区专线| 宅男噜噜噜66在线观看| 欧美级特黄aaaaaa片| 色噜噜亚洲男人的天堂| 97久久精品无码一区二区天美 | 天天做天天爱夜夜爽导航| 永德县|