2018-11-17 18:28:24 nanfeibuyi 阅读数 8958
  • Modbus协议讲解及实现

    课程背景 Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。 本课程主讲老师集10多年在Modbus 协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6 开发板进行手把手编程实践,十分有利于初学者学习。 涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。 课程名称    <<Modbus协议讲解及实现>> 课程内容 1、Modbus 协议的基础。 2、Modbus协议栈函数编程实现。 3、Modbus 协议在串行链路编程实现。 4、Modbus协议在以太网链路编程实现。 5、常见问题的解决方法。 带给您的价值 通过学习本课程,您可以做到如下: 1 、全面彻底的理解Modbus协议。 2、理解在串行链路,以太网链路的实现。 3、掌握Modbus协议解析的函数编程方法,调试工具的使用。 4 、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。 5、掌握Modbus协议下,负数,浮点数等处理方法。 讲师简介 许孝刚,山东大学工程硕士,副高职称,技术总监。 10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。 2017年获得“华为开发者社区杰出贡献者”奖励。

    9 人正在学习 去看看 许孝刚

单片机 串口编程之串口通信仿真实验

一、简述

       记--简单的使能串口,串口收发数据的例子。(使用Proteus仿真+虚拟串口调试)

       代码,仿真文件打包:链接: https://pan.baidu.com/s/1nyb46fTJrYcAy_VarFdO3A 提取码: j44s 

         蓝奏:https://www.lanzous.com/i2fx3oh

      虚拟串口调试软件打包:链接: https://pan.baidu.com/s/1qaOgM8P7ZRmXb903NkiMOQ 提取码: r18u 

         蓝奏:https://www.lanzous.com/i2fx41a

       串口助手源码:链接: https://pan.baidu.com/s/1T9ZA8jnsjXDhNLuL1ezGdg 提取码: 6usr 

二、效果

        PC机通过串口调试助手发送数据给单片机,单片机收到之后回复:I received.。如果单片机收到的是'1'则点亮LED灯,否则熄灭LED灯。

           

   使用c#编写的串口助手

      

  (真实硬件实验:使用Keil C51将代码编译为HEX文件,用串口线/USB转串口线连接PC机,可以使用STC-ISP软件烧写到51单片机,在PC机可以使用串口调试助手(STC-ISP也有这个功能,也可以是其他软件,当然也可以自己编写串口程序)与单片机进行串口通信。实际中,电脑串口采用232电平,而单片机串口则采用TTL电平,如果不进行电平转换,单片机与电脑就无法正常通信,甚至单片机芯片可能会被烧坏。MAX232 芯片可以进行电平转换,是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。​​​​电平是个电压范围,如可能规定输出电压>2.4V则为高电平,输出电压低于<0.4V则为低电平,不同的电子器件、不同的标准有着不同的电压范围​​​)

       注:一个COM一般不能同时被两个程序占用。

三、工程结构及各属性设置

        keil工程

         

         

         

          

        proteus仿真

        

添加虚拟串口

                                

四、源文件

       main.c文件 

#include <reg51.h>
sbit p1_0 = P1^0;

//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
	SCON = 0x50;		        // SCON: 方式 1, 8-bit, 允许接收数据 
	TMOD |= 0x20;               // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
	TH1 = 0xFD;               // TH1:  初始值为0xFD  波特率:9600 晶振频率:11.0592MHz  
	TL1 = 0x0;
	TR1 = 1;                  // TR1:  开启定时器1                         
	EA  = 1;                  //打开总中断
	ES  = 1;                  //打开串口中断
}       

           
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
	SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
	while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
	TI = 0;// 将TI清零,表示可以发送下一字节数据。
}

// 发送字符串
void uart_send_str(unsigned char *s)
{
	while(*s != '\0')// '\0':字符串结束标志
	{
		uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
		s++;// 指向下一个字符
	}
}


// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 		//也叫串行中断服务程序
{
	unsigned char recv_data;// 用来存放接收到的数据
	unsigned char send_data[] = "I received.\n";// 要发送的信息
	
	if(RI) //接收数据(1字节)完毕,RI会被硬件置1
	{
		RI = 0;            		// 将 接收中断标志位 清零(让串口可以继续接收数据)
		recv_data = SBUF;           	//读取接收到的数据,并存放到data
	
		
		if(recv_data == '1')//如果收到的字符是'0',就让灯灭
		{
			p1_0 = 0;//  p1.0引脚低电平,LED灯亮
		}
		else
		{
			p1_0 = 1;// p1.0引脚高电平,LED灯灭
		} 

	    uart_send_str(send_data); //收到数据之后,发送字符串"I received."给对方
	}
	if(TI)// 发送数据(1字节)完毕
	{
		TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
	}
} 

void main(void)
{
	init_uart();// 初始化串口
	
	while(1)// 主循环不做任何动作。(死循环)
	{}
}

五、总结

       51单片机系列

      1、单片机的串口相关的寄存器

            1) 串口控制寄存器SCON:(Serial CONtrol)

            2) 串行数据寄存器SBUF:(Serial data BUFfer)

            3)电源管理寄存器PCON:(Power CONtrol)

      2、串口控制寄存器SCON

            用来对串口进行控制,其寄存器地址为0x98,支持位寻址。

            一般来说单片机的寄存器的个数在制作好就已经确定了,MCS-51单片机有一些寄存器是8位的(可以存放8位数据),有一些是16位的,这些寄存器有点像全局变量(寄存器名-变量名),它们分配了固定的地址空间,我们可以对这个寄存器变量进行赋值或读取值。比如我们禁止串口中断,那么我们可以设置相应的值,当系统处理串口相关的任务时,读取到这个我们设置的值时,直到我们要禁止串口中断,那么系统就不会相应串口中断。串口控制寄存器SCON的地址为0x98,我们可以直接对SCON这个"变量"进行赋值,支持位寻址意思是:SCON一共有8位,每1位代表的意思都有不同,可以直接单独对某一位进行设置,有一些寄存器是不可以位寻址的。

           

SCON控制寄存器
位编号

D7

D6

D5

D4

D3

D2

D1

D0

位名称

SM0

SM1

SM2

REN

TB8

RB8

TI

RI

位地址

9FH

9EH

9DH

9CH

9BH

9AH

99H

98H

 

SM0,SM1串行口工作方式控制位

SM0

SM1

工作方式

功能

波特率

0

0

方式0

8位同步移位寄存器

晶振频率/12

0

1

方式1

10位UART

可变

1

0

方式2

11位UART

晶振频率/64或晶振频率/32

1

1

方式3

11位UART

可变

 

SM2 多机通信控制位 当该位置1为多机通信,多机通信仅在方式2、方式3有效。
REN 接收允许位  为1允许接收,为0禁止接收
TB8 待发送的第9位数据  同来存放工作方式2,工作方式3模式下等待发送的第9位数据
RB8 收到的第9位数据 同来存放工作方式2,工作方式3模式下接收到的第9位数据,在方式1下为接收到的停止位,工作当时0不使用该位。
TI 发送中断标志位  发送完成标志位,当SBUF中的数据发送完成后由硬件置1,并且当单片机硬件中断被使能之后触发中断事件,该位必须由软件清零,并且该位被清零之后才能进行下一个字节数据的发送。
RI 接收中断标志位 接受完成标志位,当SBUF收到一个字节的数据后由硬件系统置1,并且当单片机硬件中断被使能之后触发串行中断事件,该位必须由软件清零,并且只有该位被清零之后才能够进行下一字节数据的接收。

      3、串行数据寄存器SBUF

            用于存放串行通信中发送和接收的相关数据。其寄存器地址是0x99。

            SBUF由发送缓冲寄存器和接收缓冲寄存器组成,这两个寄存器占用同一个寄存器地址。其中发送缓冲寄存器只写,接收寄存器只读。

           发送数据:当将一个数据写入SBUF后,单片机立即根据选择的工作方式和波特率将写入的字节数据进行相应的处理然后从TXD(P3.1)引脚串行发送出去,发送完成之后置位相应寄存器的标志位,只有相应的标志位被清除之后才能进行下一次的数据发送。

           接收数据:当RXD(P3.0)引脚根据工作方式和波特率接收到一个完整的数据字节后单片机将该数据字节放入接收缓冲寄存器中,并置位相应的标志位。接收缓冲寄存器是双字节的,这样就可以在单片机读取接收缓冲寄存器的数据时同时进行下一个字节的数据接收。不会发生前后两个字节的数据冲突(数据覆盖)。

      4. 电源管理寄存器PCON

            可以控制串口波特率加倍。

                                                                                         PCON电源管理寄存器

位编号

D7

D6

D5

D4

D3

D2

D1

D0

位符号

SMOD

SMOD0

LVDF

POF

GF1

GF0

PD

IDL

      SMOD:该位与串口通信有关。

      SMOD=0; 串口方式1,2,3时,波特率正常。

      SMOD=1; 串口方式1,2,3时,波特率加倍。

      5、串口的工作方式

            MCS-51单片机的串口一共4种工作方式。其中工作方式0为同步通信方式,其余3种为异步通信。

            工作方式0:外部引脚TXD(P3.0)为数据的输出输入端,外部引脚TXD(P3.1)提供数据的同步脉冲。

                                因此工作在方式0进行串口通信不需要考虑波特率。

           工作方式1:使用定时器作为波特率发生器。

                               波特率的计算  

                                              

                                                                          常见的波特率对应的初始值
波特率/工作频率 11.0592MHz 12MHz

14.74456MHz

16MHz 20MHz SMOD值
150(b/s) 0x40 0x30 0x00     0
300(b/s) 0xA0 0x98 0x80 0x75 0x52 0
600(b/s) 0xD0 0xCC 0xC0 0xBB 0xA9 0
1200(b/s) 0xF8 0xE6 0xE0 0xDE 0xD5 0
2400(b/s) 0xF4 0xF3 0xF0 0xEF 0xEA 0
4800(b/s)   0xF3 0xEF 0xEF   1
4800(b/s) 0xFA   0xF8   0xF5 0
9600(b/s) 0xFD   0xFC     0
9600(b/s)         0xF5 1
19200(b/s) 0xFD   0xFC     1

      工作方式2:

                                                                     

      工作方式3:波特率计算方式与工作方式1相同。 

     

      6.波特率

           波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示。

           在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。

           通信双方约定一致的波特率以便正确的接收数据。(数据发送前可能进行了调制,波特率不同,调制参数不同)

          如果收发双方的波特率不一样,那么调制与解调的参数不一样,就很有可能读取不到正确的数据,从而解读为乱码。

          通常串口工作方式1使用定时器作为波特率发生器。

      7、串口中断

            使能串口中断:打开总中断-中断控制寄存器IE的EA位置1,打开串口中断中断控制寄存器IE的ES位置1

                                                                                               中断控制寄存器IE(Interrupt Enable

位序号

D0

D1

D2

D3

D4

D5

D6

D7

说明

外部中断0

定时/计数0

外部中断1

定时/计数1

串行口中断

保留位

保留位

全局中断位

位符号

EX0

ET0

EX1

ET1

ES

--

--

EA

位地址

A8H

A9H

AAH

ABH

ACH

--

--

AFH

EX0 外部中断0允许位 EX0=1,允许外部中断0中断;EX0=0,禁止外部中断0中断。
ET0 T0溢出中断允许位 ET0=1,允许T0中断;ET0=0,禁止T0中断。
EX1 外部中断1允许位 EX1=1,允许外部中断1中断;EX1=0,禁止外部中断1中断。
ET1 T1溢出中断允许位 ET1=1,允许T1中断;ET1=0,禁止T1中断。
ES

串行中断允许位

(Enable Serial)

ES=1,允许串行口中断;ES=0,禁止串行口中断。
EA

中断总允许位

(Enable All)

EA=1,CPU开放中断;EA=0,CPU禁止所有的中断请求。总允许EA好比一个总开关。

 

     使能串口中断之后,接收到数据(RI置1)/完成发送(TI置1),RI/TI被置1就会触发串口中断事件,然后执行中断处理函数。

     中断处理函数编写格式:               

void 函数名(void) interrupt 4 using 工作寄存器组编号
{
    //要执行的动作
}

      4的含义: 一共5个中断编号为0~4,串口中断的编号为4

      using 寄存器组编号:一共4组工作寄存器组,编号为0~3,如果不写默认为工作寄存器组0。

8、初始化串口为模式1过程。

      1) SCON设置

位名称

SM0

SM1

SM2

REN

TB8

RB8

TI

RI

设置值

0

1

0

1

0

0

0

0

含义 工作在方式1 波特率不加倍 允许接收数据 工作方式1不设置该位 初始为未发送完数据

初始为为接收到数据

      SCON = 0x01010000 = 0x50    

       2) PCON设置

       

位编号

D7

D6

D5

D4

D3

D2

D1

D0

位符号

SMOD

SMOD0

LVDF

POF

GF1

GF0

PD

IDL

默认波特率不加倍 0 0 0 0 0 0 0 0
波特率加倍 1 0 0 0 0 0 0 0

      PCON = 0x00 (SMOD=0,波特率不加倍)

      PCON = 0x80 (SMOD=1,波特率加倍)

      3) 定时器设置

              工作方式设置:

                                                                  MI,M0定时器模式选择

M1

M0

工作方式

功能说明

0

0

方式0

13位定时器/计数器

0

1

方式1

16位定时器/计数器

1

0

方式2

自动重载8位定时器/计数器

1

1

方式3

T0分为2个8位独立计数器,T1无方式3

            开启定时器1:将控制寄存器TCON的TR1置1

            设置定时器1的初值:TH1数据寄存器装载初值的高8位,TL1数据寄存器装载初值的低8位。

            定时器用作波特率发生器时,使用上面的波特率计算公式得到初始值,或直接查看常见的波特率对应的初始值。

            TH1 = 0xFD;// 11.0592MHz的工作频率,9600的波特率 对应的初始值。 

        4) 打开总中断 EA = 1; 打开串口中断 ES = 1;

              

================以下回复 qq_45096251 这位兄弟==============

两个单片机之间的串口通信仿真实验:

测试的仿真文件和代码:链接: https://pan.baidu.com/s/1PMu6dT-1DMEJllgv9gxqMA 提取码: hrcy

使用虚拟串口软件,虚拟出两对串口,例子中是com1<==>com2,  com3<==>com4,其中com2,com4分别对应单片机A和单片机B。com1和com3分别是PC端与单片机A、B通信的端口。在PC端使用串口工具实现两个仿真单片机的串口通信。

测试效果:

2019-03-04 21:01:08 a568713197 阅读数 1578
  • Modbus协议讲解及实现

    课程背景 Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。 本课程主讲老师集10多年在Modbus 协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6 开发板进行手把手编程实践,十分有利于初学者学习。 涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。 课程名称    <<Modbus协议讲解及实现>> 课程内容 1、Modbus 协议的基础。 2、Modbus协议栈函数编程实现。 3、Modbus 协议在串行链路编程实现。 4、Modbus协议在以太网链路编程实现。 5、常见问题的解决方法。 带给您的价值 通过学习本课程,您可以做到如下: 1 、全面彻底的理解Modbus协议。 2、理解在串行链路,以太网链路的实现。 3、掌握Modbus协议解析的函数编程方法,调试工具的使用。 4 、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。 5、掌握Modbus协议下,负数,浮点数等处理方法。 讲师简介 许孝刚,山东大学工程硕士,副高职称,技术总监。 10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。 2017年获得“华为开发者社区杰出贡献者”奖励。

    9 人正在学习 去看看 许孝刚

前言

    串口是我们特别常用的一个通讯接口,在Linux下的串口编程使用的就是文件IO的操作,所以说这次来进行串口的应用编程,实现发送接收功能

实战Linux串口编程

    在单片机串口编程中,需要用户直接对寄存器以及中断进行控制

    而在Linux串口编程中,无论是官方直接下载的原生内核还是厂家提供的Linux内核都会将串口驱动写好,所以不需要自己写串口驱动

    Linux串口应用编程使用原厂提供的接口进行初始化配置以及发送和接收

    串口基本介绍

        一次只传送一个数据位,以单个bit的方式传输

        一般特指RS232

    流控

        两个串行接口之间的传输数据流通常需要协调一致才行,这可能是由于用于通信的某个串行接口或者某些存介质的中间通信链路的限制造成的

        软件流控制:采用特殊字符开始和结束,开始(XON,DC1,八进制数021),结束(XOFF,DC3,八进制数023),这些字符都在ASCII中定义好了,对于传输文本信息非常有用,不能用于在特殊程序中的其他类型信息

        硬件流控制:使用RS-232标准的CTS和RTS信号来取代之前的特殊字符,当准备就绪时接收方会将CTS设置为space电压,尚未就绪时设置为mark电压;相应的发送方会在准备就绪时将RTS设置为space电压。

        软件流控的速度更快,但并不是所有的硬件和操作系统都支持CTS/RTS流控制

    串口编程的流程

    

       打开串口:一般使用open函数,打开后返回句柄,这个句柄就可以提供给发送和接收函数使用

        初始化串口:需要设置波特率、校验位、数据位、停止位等一系列的参数,初始化比较麻烦,要知道如何进行研制和配置

       发送和接收数据:属于字符设备,可以使用read函数和write函数实现

       关闭:使用函数close即可关闭串口

    设置开机启动

        在/etc/init.d/rcS最后添加要执行的程序

       

    打开串口

       确认设备节点

       

        在4412开发板中设备节点使用的是ttySAC*系列

       有四个串口

       uartopen.c

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

main(){
	int fd;
	char *leds = "/dev/leds";
	char *test1 = "/bin/test1";
	char *test2 = "/bin/test2";
	
	if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){
		printf("open %s failed!\n",leds);
	}
		printf("\n%s fd is %d\n",leds,fd);
		
	if((fd = open(test1,O_RDWR,0777))<0){
		printf("open %s failed!\n",test1);
	}
		printf("%s fd is %d\n",test1,fd);	
		
	if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed!\n",test2);
	}
		printf("%s fd is %d\n",test2,fd);	
}

         运行结果

       

    串口初始化参数介绍

        常用的:串口号、波特率、数据位、停止位、校验位、流控

        查看/arch/arm/include/asm中的termios.h

struct termio {
unsigned short c_iflag;                /* input mode flags */
unsigned short c_oflag;                /* output mode flags */
unsigned short c_cflag;                /* control mode flags */
unsigned short c_lflag;                /* local mode flags */
unsigned char c_line;                /* line discipline */
unsigned char c_cc[NCC];        /* control characters */
};

         c_iflag输入模式标志

        c_oflag输出模式标志

        c_cflag控制模式标志

         c_lflag本地模式标志

    串口初始化常用函数

        头文件<termios.h><unistd.h>

        函数tcgetattr

            读取当前穿裤的参数值,一般用于先确认该串口是否能配置,做检测用

            int tcgetattr(int fd,struct termios *termios_p)

            先创建一个termios结构体来存储旧的参数

        波特率相关函数

            函数cfsetisspeed和cdsetosspeed用于修改串口的波特率,函数cfgetisspeed和cfgetosspeed用于获取当前的波特率

            设置函数

           int cfsetisspeed(struct termios *termios_p,speed_t speed);

            speed:波特率,常用的B2400,B4800,B9600,B115200等,

            int cdsetosspeed(struct termios *termios_p,speed_t speed);

            获取函数

            speed_t cfgetisspeed(const struct termios *termios_p)

            speed_t cfgetosspeed(const struct termios *termios_p)

        函数tcflush

            清空缓存寄存器,清空串口中没有完成的输入或者输出数据,在接收和发送数据时,串口寄存器会缓存数据

            int tcflush(int fd,int queue_selector)

            fd是open返回的文件句柄

            queue_selector控制tcflush的操作

            三个常用的数值,TCIFLUSH清除正受到的数据,且不会读取出来;TCOFLUSH清除正写入的数据,且不会发送至终端;TCIOFLUSH清除所有正在发生的IO数据

        函数tcsetattr

           int tcsetattr(int fd,int optional_actions,const struct termios*termios_p)

            optional_actions:参数生效时间

            三个常用的值:TCSANOW,不等数据传输完毕就立即改变属性;TCSADRAIN,等待所有数据传输结束才改变属性;TCSAFLUSH,清空输入输出缓冲区才改变属性

            termios_p在旧的参数基础上修改后的参数

    初始化流程分析

        

    串口发送

        uartwrite.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

int set_opt(int,int,int,char,int);
void main()
{
	int fd,wr_static,i=10;
	char *uart3 = "/dev/ttySAC3";
	char *buffer = "hello world!\n";
	
	printf("\r\nitop4412 uart3 writetest start\r\n");
	
	if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
		printf("open %s is failed",uart3);
	}
	else{
		printf("open %s is success\n",uart3);
		set_opt(fd, 115200, 8, 'N', 1); 
		//发送十次数据
		while(i--)
		{
			wr_static = write(fd,buffer, strlen(buffer));
			if(wr_static<0)
				printf("write failed\n");
			else{
				printf("wr_static is %d\n",wr_static);
			}
			sleep(1);
		}
	}
	close(fd);
}


int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;//定义结构体newtio和oldtio
	//测试串口能否运行并将原串口的数据取到oldtio
	if  ( tcgetattr( fd,&oldtio)  !=  0) { 
		perror("SetupSerial 1");
		return -1;
	}
	//将newio清零和设置c_cflag
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD;//使能接收和忽略控制线
	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;
	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;//读取最小字符数为0
	tcflush(fd,TCIFLUSH);//清空缓冲区
	//进行初始化设置
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
//	printf("set done!\n\r");
	return 0;
}


        运行结果(使用串口助手)

       

    串口接收

        uartread.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口
void main()
{
	int fd,nByte;
	char *uart3 = "/dev/ttySAC3";
	char buffer[512];
	char *uart_out = "please input\r\n";
	memset(buffer, 0, sizeof(buffer));
	if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
		printf("open %s is failed",uart3);
	else{
		set_opt(fd, 115200, 8, 'N', 1);
		//发送输入提醒
		write(fd,uart_out, strlen(uart_out));
		while(1){
				//如果串口读到数据
			while((nByte = read(fd, buffer, 512))>0){
				buffer[nByte+1] = '\0';//在数据后加结束符			
				write(fd,buffer,strlen(buffer));//把接收到的发送回去
				memset(buffer, 0, strlen(buffer));//将接受buffer清空
				nByte = 0;
			}
		}
	}
}

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;
	if  ( tcgetattr( fd,&oldtio)  !=  0) { 
		perror("SetupSerial 1");
		return -1;
	}
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD;
	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;
		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(fd,TCIFLUSH);
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
	
	//	printf("set done!\n\r");
	return 0;
}


        运行结果

       

2019-07-25 16:40:42 qq_36931762 阅读数 168
  • Modbus协议讲解及实现

    课程背景 Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。 本课程主讲老师集10多年在Modbus 协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6 开发板进行手把手编程实践,十分有利于初学者学习。 涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。 课程名称    <<Modbus协议讲解及实现>> 课程内容 1、Modbus 协议的基础。 2、Modbus协议栈函数编程实现。 3、Modbus 协议在串行链路编程实现。 4、Modbus协议在以太网链路编程实现。 5、常见问题的解决方法。 带给您的价值 通过学习本课程,您可以做到如下: 1 、全面彻底的理解Modbus协议。 2、理解在串行链路,以太网链路的实现。 3、掌握Modbus协议解析的函数编程方法,调试工具的使用。 4 、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。 5、掌握Modbus协议下,负数,浮点数等处理方法。 讲师简介 许孝刚,山东大学工程硕士,副高职称,技术总监。 10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。 2017年获得“华为开发者社区杰出贡献者”奖励。

    9 人正在学习 去看看 许孝刚

51单片机心形灯——实现串口通信编程

即其中一个单片机的按键控制另外一个单片机上的灯亮灭
心形灯效果图 如果是静态效果请刷新页面哦
在这里插入图片描述
心形灯原理图
在这里插入图片描述

#include <reg52.h>

sbit k1=P3^2;
sbit k2=P3^3;
sbit k3=P3^4;
sbit k4=P3^5;

#define LED1  P0
#define LED2  P2

void UsartInit(){
	SCON=0x50;
	TMOD=0x20;
	PCON=0x80;
	TH1=0xF4;
	TL1=0xF4;
	ES=1;
	EA=1;
	TR1=1;
}

void delay(unsigned int ms)
{
	unsigned int i,j;
	for(i=0;i<ms;i++)
		for(j=0;j<110;j++);
}


unsigned char keyscan(void){
	unsigned char key='.';
	if (k1==0)
	{
		delay(1);
		if(k1==0)
		{
			key='1';
			while(!k1);
		}
	}
	
	if (k2==0)
	{
		delay(1);
		if(k2==0)
		{
			key='2';
			while(!k2);
		}
	}
	
	if (k3==0)
	{
		delay(1);
		if(k3==0)
		{
			key='3';
			while(!k3);
		}
	}
	
	if (k4==0)
	{
		delay(1);
		if(k4==0)
		{
			key='4';
			while(!k4);
		}
	}
	
	return key;
}

void Tras(unsigned char key)
{
	SBUF=key;
	while(!TI);
	TI=0;
}

void main()
{
	
	unsigned char key;
	UsartInit();
	while(1)
	{
		key=keyscan();
		if(key!='.')
		{
			Tras(key);
			key='.';
		}
	}	
}

接收方:

#include <reg52.h>
#define uchar unsigned char;
#define uint unsigned int;

uchar receiveData;
uchar flag;
uchar code table[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0xff};
sbit LED17=P3^6;
void UsartInit(){
	SCON=0x50;
	TMOD=0x20;
	PCON=0x80;
	TH1=0xF4;
	TL1=0xF4;
	ES=1;
	EA=1;
	TR1=1;
}

void Delay(unsigned int a){
	unsigned char b;
	for(;a>0;a--){
		for(b=110;b>0;b--);
	}
}

void AllOn(){
	P0=0x00;
	P2=0x00;
	LED17=0;
}

void AllDown(){
	P0=0xff;
	P2=0xff;
	LED17=1;
}

void RFlow(){
	int i;

		for(i=0;i<=7;i++){

			P2=table[i];
			Delay(300);
		}
		
		P2=table[8];
		for(i=7;i>=0;i--){
			P0=table[i];
			//P2=table[i];
			Delay(300);
		}
		P0=table[8];
		
		LED17=0;
		Delay(300);
		LED17=1;
}

void LFlow(){
	 int i;

		for(i=0;i<=7;i++){
			P0=table[i];
			//P2=table[i];
			Delay(300);
		}
		P0=table[8];
		for(i=7;i>=0;i--){
			//P0=table[i];
			P2=table[i];
			Delay(300);
		}
		
		P2=table[8];
		LED17=0;
		Delay(300);
		LED17=1;
}
void main(){
	UsartInit();
	while(1)
	{
			switch(receiveData)
			{
				case '1': AllOn();break;
				case '2': AllDown();break;
				case '3': LFlow();break;
				case '4': RFlow();break;
				default:break;
			}
	}
}


void Usart() interrupt 4{
	receiveData=SBUF;
	RI=0;
	
	P0=0xff;
	P2=0xff;
	LED17=1;
}

存在我心形灯效果图与代码放错不匹配的情况,如果你按照我的代码装载运行效果与我效果图不一样请评论告诉我!我找一下改一下,如果我后期把相应的效果视频删了,那么就emmmmm意会~意会~我觉得你很聪明肯定懂我的意思hiahia

2017-04-03 13:43:39 hokamyuen 阅读数 676
  • Modbus协议讲解及实现

    课程背景 Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。 本课程主讲老师集10多年在Modbus 协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6 开发板进行手把手编程实践,十分有利于初学者学习。 涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。 课程名称    <<Modbus协议讲解及实现>> 课程内容 1、Modbus 协议的基础。 2、Modbus协议栈函数编程实现。 3、Modbus 协议在串行链路编程实现。 4、Modbus协议在以太网链路编程实现。 5、常见问题的解决方法。 带给您的价值 通过学习本课程,您可以做到如下: 1 、全面彻底的理解Modbus协议。 2、理解在串行链路,以太网链路的实现。 3、掌握Modbus协议解析的函数编程方法,调试工具的使用。 4 、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。 5、掌握Modbus协议下,负数,浮点数等处理方法。 讲师简介 许孝刚,山东大学工程硕士,副高职称,技术总监。 10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。 2017年获得“华为开发者社区杰出贡献者”奖励。

    9 人正在学习 去看看 许孝刚

接触过linux编程的应该都知道,学习linux应用最好的参考资料就是系统自带的手册——通过man命令查找编程手册。

通过搜索引擎搜索与linux串口编程相关的关键字,找到与串口编程相关的结构体或者函数就可以开始自学串口应用编程了。

学习环境:deepin 15.3 64位系统,开发板:nanopc-t1,运行ubuntu 14.04LTS,内核版本:3.8.13.16,交叉编译链:arm-linux-gnueabihf-gcc,版本4.7.3

我是从串口相关的termios结构体开始入手的,终端中输入man 3 termios

hokamyuen@hokamyuen-deepin:~$ man 3 termios

就能打开串口编程手册了,学习过程中遇到的串口相关的问题都可以在这里找到答案。

打开手册,可以看到串口相关的许多函数,而且很多函数都含有struct termios这个结构体,所以这个结构体一定是重点,继续往下翻手册可以看到这个结构体的介绍

DESCRIPTION
       The termios functions describe a general terminal interface that is provided to control asynchronous  communi‐
       cations ports.
   The termios structure
       Many of the functions described here have a termios_p argument that is a pointer to a termios structure.  This
       structure contains at least the following members:

           tcflag_t c_iflag;      /* input modes */
           tcflag_t c_oflag;      /* output modes */
           tcflag_t c_cflag;      /* control modes */
           tcflag_t c_lflag;      /* local modes */
           cc_t     c_cc[NCCS];   /* special characters */

描述中说到,termios这个结构体是用于描述一个串口的各个参数的,结构体包含输入,输出模式等参数。不过,这些参数跟学单片机时配置的参数完全不一样,而且最重要的一点貌似没有——这个结构体描述的是哪个串口。继续翻手册,又发现了一个读取设置和生效配置的说明

Retrieving and changing terminal settings
       tcgetattr() gets the parameters associated with the object referred by fd  and  stores  them  in  the  termios
       structure  referenced by termios_p.  This function may be invoked from a background process; however, the ter‐
       minal attributes may be subsequently changed by a foreground process.

       tcsetattr() sets the parameters associated with the terminal (unless support is required from  the  underlying
       hardware  that is not available) from the termios structure referred to by termios_p.  optional_actions speci‐
       fies when the changes take effect:
这样的话我们只需要修改跟我们预期的配置不一样的地方然后再生效配置就好了。那首先我们就要开始使用这个函数看看串口原本的参数了。

回到手册的头部

SYNOPSIS
       #include <termios.h>
       #include <unistd.h>

       int tcgetattr(int fd, struct termios *termios_p);

可以看到所有的函数生明和需要的头文件,首先把手册列出来的头文件添加到我们的代码中

#include <termios.h>
#include <unistd.h>

int main(void)
{
    return 0;
}

再看tcgetattr函数的第一个入口参数为fd,很明显这个为文件描述符,不难想到我们要控制的是哪个串口就是由这个参数来决定的。文件描述符在打开文件的时候生成,所以我们需要打开串口设备,同样利用man 命令查找open函数的使用方法和需要的头文件

hokamyuen@hokamyuen-deepin:~$ man 3 open
SYNOPSIS
       #include <sys/stat.h>
       #include <fcntl.h>


       int open(const char *path, int oflag, ...);
       int openat(int fd, const char *path, int oflag, ...);
可以看到open函数需要sys/stat.h,fcntl.h两个头文件,open函数的第一个入口参数为文件的路径,至于第二个参数oflag我们可以继续翻手册寻找可以选用的参数
Values for oflag are constructed by a bitwise-inclusive OR of  flags  from  the  following  list,  defined  in
       <fcntl.h>.   Applications  shall specify exactly one of the first five values (file access modes) below in the
       value of oflag:

       O_EXEC        Open for execute only (non-directory files). The result is unspecified if this flag  is  applied
                     to a directory.

       O_RDONLY      Open for reading only.

       O_RDWR        Open for reading and writing. The result is undefined if this flag is applied to a FIFO.

       O_SEARCH      Open  directory  for  search  only.  The result is unspecified if this flag is applied to a non-
                     directory file.

       O_WRONLY      Open for writing only.

       Any combination of the following may be used:

       O_APPEND      If set, the file offset shall be set to the end of the file prior to each write.

       O_CLOEXEC     If set, the FD_CLOEXEC flag for the new file descriptor shall be set.

       O_CREAT       If the file exists, this flag has no effect except as noted under O_EXCL below.  Otherwise,  the
                     file  shall  be  created;  the  user ID of the file shall be set to the effective user ID of the
                     process; the group ID of the file shall be set to the group ID of the file's parent directory or
                     to  the  effective group ID of the process; and the access permission bits (see <sys/stat.h>) of
                     the file mode shall be set to the value of the argument following the oflag  argument  taken  as
                     type mode_t modified as follows: a bitwise AND is performed on the file-mode bits and the corre‐
                     sponding bits in the complement of the process' file mode creation mask. Thus, all bits  in  the
                     file  mode  whose corresponding bit in the file mode creation mask is set are cleared. When bits
                     other than the file permission bits are set, the effect is unspecified. The  argument  following
                     the  oflag  argument does not affect whether the file is open for reading, writing, or for both.
                     Implementations shall provide a way to initialize the file's group ID to the  group  ID  of  the
                     parent  directory.  Implementations  may, but need not, provide an implementation-defined way to
                     initialize the file's group ID to the effective group ID of the calling process.

       O_DIRECTORY   If path resolves to a non-directory file, fail and set errno to [ENOTDIR].

       O_DSYNC       Write I/O operations on the file descriptor shall complete as defined by synchronized  I/O  data
                     integrity completion.

       O_EXCL        If O_CREAT and O_EXCL are set, open() shall fail if the file exists. The check for the existence
                     of the file and the creation of the file if it does not exist shall be atomic  with  respect  to
                     other  threads  executing  open() naming the same filename in the same directory with O_EXCL and
                     O_CREAT set. If O_EXCL and O_CREAT are set, and path names a symbolic link,  open()  shall  fail
                     and set errno to [EEXIST], regardless of the contents of the symbolic link. If O_EXCL is set and
                     O_CREAT is not set, the result is undefined.

       O_NOCTTY      If set and path identifies a terminal device, open() shall not  cause  the  terminal  device  to
                     become  the  controlling  terminal for the process. If path does not identify a terminal device,
                     O_NOCTTY shall be ignored.

       O_NOFOLLOW    If path names a symbolic link, fail and set errno to [ELOOP].

       O_NONBLOCK    When opening a FIFO with O_RDONLY or O_WRONLY set:

                      *  If O_NONBLOCK is set, an open() for reading-only shall return without delay. An  open()  for
                         writing-only shall return an error if no process currently has the file open for reading.

                      *  If  O_NONBLOCK  is  clear, an open() for reading-only shall block the calling thread until a
                         thread opens the file for writing. An open() for writing-only shall block the calling thread
                         until a thread opens the file for reading.

                     When opening a block special or character special file that supports non-blocking opens:

                      *  If O_NONBLOCK is set, the open() function shall return without blocking for the device to be
                         ready or available. Subsequent behavior of the device is device-specific.

                      *  If O_NONBLOCK is clear, the open() function shall block the calling thread until the  device
                         is ready or available before returning.

                     Otherwise,  the O_NONBLOCK flag shall not cause an error, but it is unspecified whether the file
                     status flags will include the O_NONBLOCK flag.
O_RSYNC       Read I/O operations on the file descriptor shall complete at the  same  level  of  integrity  as
                     specified by the O_DSYNC and O_SYNC flags. If both O_DSYNC and O_RSYNC are set in oflag, all I/O
                     operations on the file descriptor shall complete as defined by synchronized I/O  data  integrity
                     completion. If both O_SYNC and O_RSYNC are set in flags, all I/O operations on the file descrip‐
                     tor shall complete as defined by synchronized I/O file integrity completion.

       O_SYNC        Write I/O operations on the file descriptor shall complete as defined by synchronized  I/O  file
                     integrity completion.

                     The  O_SYNC flag shall be supported for regular files, even if the Synchronized Input and Output
                     option is not supported.

       O_TRUNC       If the file exists and is a regular  file,  and  the  file  is  successfully  opened  O_RDWR  or
                     O_WRONLY,  its  length  shall  be  truncated to 0, and the mode and owner shall be unchanged. It
                     shall have no effect on FIFO special files or terminal device files. Its effect  on  other  file
                     types  is  implementation-defined. The result of using O_TRUNC without either O_RDWR or O_WRONLY
                     is undefined.

       O_TTY_INIT    If path identifies a terminal device other than a pseudo-terminal, the  device  is  not  already
                     open  in  any  process,  and either O_TTY_INIT is set in oflag or O_TTY_INIT has the value zero,
                     open() shall set any non-standard termios structure terminal parameters to a state that provides
                     conforming  behavior;  see the Base Definitions volume of POSIX.1‐2008, Section 11.2, Parameters
                     that Can be Set.  It is unspecified whether O_TTY_INIT has any effect if the device  is  already
                     open  in any process. If path identifies the slave side of a pseudo-terminal that is not already
                     open in any process, open() shall set any non-standard termios structure terminal parameters  to
                     a state that provides conforming behavior, regardless of whether O_TTY_INIT is set. If path does
                     not identify a terminal device, O_TTY_INIT shall be ignored.
这里我选择了读写权限标志O_RDWR和非控制终端标志O_NOCTTY两个选项,打开文件这句代码的参数就找齐了,打开文件后要记得关闭

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{	
	int fd;
	char tty_addr[] = "/dev/ttySAC2";
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	close(fd);
	return 0;
}

每做完一步先编译并且放上开发板测试一遍,我用的是nanopc-t1

hokamyuen@hokamyuen-deepin:~/nanopc/usart-test$ arm-linux-gnueabihf-gcc usart-test.c -o usart-test -static
没有错误提示而且生成了usart-test文件就证明编译成功了。用自己决觉得方便的方法吧程序下载的板子里,我比较喜欢用scp方式。运行程序并观察输出结果

root@NanoPC:~# ./usart-test 
打开/dev/ttySAC2成功
root@NanoPC:~# 
可以看到,程序顺利的运行了,接着进行下一步——获取串口的参数。我们首先看看串口的波特率。

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{	
	int fd,uart_in_speed,uart_out_speed;
	char tty_addr[] = "/dev/ttySAC2";
	struct termios uart_setting_struct;
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	tcgetattr(fd,&uart_setting_struct);
	uart_in_speed = cfgetispeed(&uart_setting_struct);
	uart_out_speed = cfgetospeed(&uart_setting_struct);
	printf("串口输入速度:%d,输出速度:%d\r\n",uart_in_speed,uart_out_speed);
	close(fd);
	return 0;
}
编译并下载到板子上运行

root@NanoPC:~# ./usart-test 
打开/dev/ttySAC2成功
串口输入速度:13,输出速度:13
root@NanoPC:~# 
运行之后可以看到串口的输入和输出的速度都不是常见的波特率,有疑惑就要重新去翻看手册寻找答案。

Line speed
       The baud rate functions are provided for getting and setting the values of the input and output baud rates  in
       the termios structure.  The new values do not take effect until tcsetattr() is successfully called.

       Setting  the speed to B0 instructs the modem to "hang up".  The actual bit rate corresponding to B38400 may be
       altered with setserial(8).

       The input and output baud rates are stored in the termios structure.

       cfgetospeed() returns the output baud rate stored in the termios structure pointed to by termios_p.

       cfsetospeed() sets the output baud rate stored in the termios structure pointed  to  by  termios_p  to  speed,
       which must be one of these constants:

            B0
            B50
            B75
            B110
            B134
            B150
            B200
            B300
            B600
            B1200
            B1800
            B2400
            B4800
            B9600
            B19200
            B38400
            B57600
            B115200
            B230400

       The  zero  baud  rate,  B0,  is used to terminate the connection.  If B0 is specified, the modem control lines
       shall no longer be asserted.  Normally, this will disconnect the line.  CBAUDEX  is  a  mask  for  the  speeds
       beyond those defined in POSIX.1 (57600 and above).  Thus, B57600 & CBAUDEX is nonzero.

       cfgetispeed() returns the input baud rate stored in the termios structure.

       cfsetispeed()  sets  the  input baud rate stored in the termios structure to speed, which must be specified as
       one of the Bnnn constants listed above for cfsetospeed().  If the input baud rate is set to  zero,  the  input
       baud rate will be equal to the output baud rate.

       cfsetspeed()  is  a  4.4BSD  extension.  It takes the same arguments as cfsetispeed(), and sets both input and
       output speed.

RETURN VALUE
       cfgetispeed() returns the input baud rate stored in the termios structure.

       cfgetospeed() returns the output baud rate stored in the termios structure.

       All other functions return:

       0      on success.

       -1     on failure and set errno to indicate the error.

       Note that tcsetattr() returns success if any of the requested  changes  could  be  successfully  carried  out.
       Therefore,  when  making multiple changes it may be necessary to follow this call with a further call to tcge‐
       tattr() to check that all changes have been performed successfully.
通过手册,我们发现串口的波特率不是直接输入数字设置的,而是通过一组宏,刚刚获取到的速度为13,即指B9600宏,表示的波特率为9600,那下一步就可以尝试修改串口的波特率了,修改设置后记得使设置生效。

Retrieving and changing terminal settings
       tcgetattr() gets the parameters associated with the object referred by fd  and  stores  them  in  the  termios
       structure  referenced by termios_p.  This function may be invoked from a background process; however, the ter‐
       minal attributes may be subsequently changed by a foreground process.

       tcsetattr() sets the parameters associated with the terminal (unless support is required from  the  underlying
       hardware  that is not available) from the termios structure referred to by termios_p.  optional_actions speci‐
       fies when the changes take effect:

       TCSANOW
              the change occurs immediately.

       TCSADRAIN
              the change occurs after all output written to fd has been transmitted.  This option should be used when
              changing parameters that affect output.

       TCSAFLUSH
              the  change  occurs after all output written to the object referred by fd has been transmitted, and all
              input that has been received but not read will be discarded before the change is made.
在tcsetattr函数的说明后面有几个生效设置选项,通过各个宏的说明,可以看到这些宏代表设置生效的时间,立刻生效,发送数据完成后生效,发送数据完成后生效而且接收到的而且没被程序读取的数据将被丢弃。按需选择就好,在当前的简单应用中,可以选择任意一个,我选择了立刻生效。源码如下:

#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define UART_BAUD B115200

int main(void)
{	
	int fd,uart_in_speed,uart_out_speed;
	char tty_addr[] = "/dev/ttySAC2";
	char send_buf[] = "hello";
	struct termios uart_setting_struct;
	fd = open(tty_addr,O_RDWR|O_NOCTTY);
	if(fd < 0)
	{
		printf("打开%s失败\r\n",tty_addr);
		return 0;
	}
	printf("打开%s成功\r\n",tty_addr);
	tcgetattr(fd,&uart_setting_struct);
	uart_in_speed = cfgetispeed(&uart_setting_struct);
	if(uart_in_speed != UART_BAUD)
	{
		cfsetispeed(&uart_setting_struct,UART_BAUD);
	}
	uart_out_speed = cfgetospeed(&uart_setting_struct);
	if(uart_out_speed != UART_BAUD)
	{
		cfsetospeed(&uart_setting_struct,UART_BAUD);
	}
	tcsetattr(fd,TCSANOW,&uart_setting_struct);
	write(fd,send_buf,strlen(send_buf));
	close(fd);
	return 0;
}
以115200的波特率打开串口调试助手,我用的是picocom,-b表示使用的波特率,/dev/ttyUSB0为usb转串口模块的串口号,ctrl+a,ctrl+q可以退出调试助手

hokamyuen@hokamyuen-deepin:~$ sudo picocom -b 115200 /dev/ttyUSB0
打开调试助手之后,把编译号好的程序下载的板子上并运行,可以在调试助手中看到输出的信息

picocom v1.7

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,

Terminal ready
hello
Thanks for using picocom
串口设置好之后就当做普通文件来读写就好了。

此文章为本人的学习过程,如有错误请指出!
















2019-07-17 21:57:41 zxc_151281 阅读数 498
  • Modbus协议讲解及实现

    课程背景 Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。 本课程主讲老师集10多年在Modbus 协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6 开发板进行手把手编程实践,十分有利于初学者学习。 涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。 课程名称    <<Modbus协议讲解及实现>> 课程内容 1、Modbus 协议的基础。 2、Modbus协议栈函数编程实现。 3、Modbus 协议在串行链路编程实现。 4、Modbus协议在以太网链路编程实现。 5、常见问题的解决方法。 带给您的价值 通过学习本课程,您可以做到如下: 1 、全面彻底的理解Modbus协议。 2、理解在串行链路,以太网链路的实现。 3、掌握Modbus协议解析的函数编程方法,调试工具的使用。 4 、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。 5、掌握Modbus协议下,负数,浮点数等处理方法。 讲师简介 许孝刚,山东大学工程硕士,副高职称,技术总监。 10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。 2017年获得“华为开发者社区杰出贡献者”奖励。

    9 人正在学习 去看看 许孝刚

1.项目背景:烟雾报警器(视频显示)  时间2019/7/17

2.项目资源:

已有:STM32最小系统、串口屏

尚缺:使用UART协议的编程能力

3.设计原理图

设计共分为四个部分

3.1MCU、串口屏、传感器

1)MCU部分使用STM32最小系统(stm32f103RC)

2)串口屏使用讨晶池串口屏(尺寸忘记)

3)烟雾传感器使用输出为数字电压传感器即可

3.2底板部分

P1:电源接口:使用Micro-USB-B母头

U1:降压模块:淘宝+5V转+3.3V 为STM32供电

P2:传感器接口:数字电路

P4:串口屏接口

P3:MCU模块接口

电平转换电路逻辑:

DVDD_OUT_5V给高时,Q1截止,DVDD_OUT_3V3高,MCU报警(不报警)

DVDD_OUT_5V给低时,VGS>Vth,Q1导通,DVDD_OUT_3V3低,MCU不报警(报警)

4.元器件选型

Android 串口编程

阅读数 215

vb串口通信编程

阅读数 1615

没有更多推荐了,返回首页