精华内容
下载资源
问答
  • 电子-参考串口解析代码.zip,单片机/嵌入式STM32-F0/F1/F2
  • HLW8032串口通讯 程序解析代码,完整的提取电压、电流、功率、电能等参数
  • 串口解析程序,本程序用c#编写,涉及北斗部分通信应用协议的解析,其中协议为北斗协议为4.0通用版目前已经民用开放,大家可放心下载使用。
  • 基于32位MCU,解析GPS协议完整代码
  • 基于16倍过采样的串口解析代码,有说明文档,发送说明文档,接收说明文档,和串口协议分析。发送和接收文档有对工程代码的逐句注释解释。
  • 边缘检测工程:串口接收模块代码解析 本文为明德扬原创文章,转载请注明出处! 串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。 注:串口波特率9600,无奇偶校验位。 ...

    边缘检测工程:串口接收模块代码解析

      本文为明德扬原创文章,转载请注明出处!
    
       串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。
    
      注:串口波特率9600,无奇偶校验位。
    

    一、设计架构
    在这里插入图片描述

    上图是与上位机通信的串口的时序图。我们从图中可以获取到如下关键信息。

    1. 串口数据线位宽为1bit,默认状态下为高电平。

    2. 每次上游模块发送数据,都是先发送1位的起始位0,然后发送8位的数据,最后是1位的停止位1。

    3. 每1位所占的时间,可以通过波特率来计算。计算方法如下:

      波特率是指1s内发送或接受了多少比特数据,若波特率为9600 bit/s,则1s可以传输9600bit,那么传输1bit的时间为:1/9600(s)。以mp801开发板为例,时钟频率为50M,那么发送1bit就需要5208个时钟周期。

    串口接收模块采用两个计数器的架构,这两个计数器分别表示接收1bit需要的时间和共需要接收多少bit。其结构图如下:
    在这里插入图片描述
    计数器cnt0:对接收1bit数据需要的时间进行计数;接收1bit需要5208个时钟周期。该计数器的计数周期为5208。

      计数器cnt1:对接收多少bit的数据进行计数;停止位不参与,起始位加上数据位共9bit。该计数器的计数周期为9。
    
    
      本工程使用了检测信号下降沿的方法,信号下降沿的检测方法:
    
      检查uart_rx的下降沿,就要用到FPGA里的边沿检测技术。所谓的边沿检测,就是检测输入信号,或者FPGA内部逻辑
      信号 的跳变,即上升沿或者下降沿的检测。就比如前面uart_rx由1变0时,就出现了下降沿,接着一次指令结束,
      uart_rx由0变1时,就出现了上升沿,边沿检测技术这在FPGA电路设计泛。电路图如下:
    

    在这里插入图片描述
    对应的信号列表如下图所示:
    在这里插入图片描述
    中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,
    就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到
    信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。
    我们来讲解这个原理,信号的波形图如下:
    在这里插入图片描述
    Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,但是相差了一个时钟周期。我们也可以理解为:
    每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigger之前的值。

    我们在看第3时钟上升沿,此时trigger值为0,而tri_ff0的值为1,即当前trigger的值为0,之前的值为1,这就是下降沿,此时neg_edge为1。当看到neg_edge为1,就表示检测到trigger的下降沿了。同理在第7个时钟上升沿,看到trigger值为1,而之前值为0,pos_edge为1,表示检测到trigger的上升沿。

      本模块输入信号uart_rx是异步信号,异步信号都需要经验同步化后,才能够使用。异步信号同步化如下。
    

    在前面讨论边沿检测的波形中,我们把trigger当做理想的同步信号来考虑,也就是trigger满足D触发器的建立和保持时间,在同步系统中实现边沿检测不是问题。但如果trigger不是理想的同步信号,例如外部按键信号,以及本工程的uart_rx信号。这些信号什么时候产生变化,是外部传输指令给FPGA,对于FPGA来说完全是随机的。很有可能出现,信号在时钟上升沿产生变化的情况,从而无法满足触发器的建立时间和保持时间要求,出现亚稳态的情况,从而导致系统崩溃。如下图,我们先将信号用2个触发器进行了寄存,确定了信号的稳定性,然后再进行边沿检测,达成了同步系统中实现边沿检测的需求。
    在这里插入图片描述
    那么边沿检测的代码设计就需要先进行触发器的设计,假设输入的信号trigger不是同步信号,要将该信号用2个
    触发器进行寄存,得到tri_ff0和tri_ff1。需要特别注意的是,在第一个触发器阶段,信号依旧有亚稳态的情况,
    因此tri_ff0绝对不可以拿来当条件使用,只能使用tri_ff1。接着进行检测边沿,根据前面所说,得出同步信号后
    再用 寄存器寄存,得到tri_ff2。根据tri_ff1和tri_ff2,我们就可以得到边沿检测结果。当tri_ff11且
    tri_ff2
    0时,上升沿的pos_edge有效;当tri_ff10且tri_ff21时,下降沿的neg_edge有效,代码如下:
    1
    2  
    3
    4     always @(posedgeclk or negedgerst_n)begin
    5        if(rst_n1’b0)begin
    6         tri_ff0 <= 0;
    7         tri_ff1 <= 0;
    8         tri_ff2 <= 0;
    9       end
    10       else begin
    11        tri_ff0 <= trigger ;
    12        tri_ff1 <= tri_ff0 ;
    13        tri_ff2 <= tri_ff1 ;
    14       end
    15     end
    16     [size=9.0000pt]
    17     assign neg_edge = tri_ff1
    0 && tri_ff21;
    18     assign pos_edge = tri_ff1
    1 && tri_ff2==0;
    19
    1

         综上所述,如果进来的信号是异步信号,那么就需要先进行同步化,再做检测,即通过打两拍的方式,实现了信号的
         同步化;再通过打一拍的方式,实现边沿检测电路。反之,如果进来的信号本身就是同步信号,那就没有必要做
         同步化了,可以直接做边沿检测。
    

    二、信号的意义

    信号            类型           意义
    clk            输入信号        时钟信号。
    rst_n           输入信号        复位信号,低电平有效。

    uart_rx          输入信号    串口接收数据线,位宽为1bit,空闲时为高电平,开始
                         收时,会变为低电平,持续1/9600(s),然后是数据、停止
                         位等。
    rx_data          输出信号    从串口中接收到的1字节数据。

    rx_vld           输出信号   串口接收数据有效指示信号。当其为高电平时,对应的输出
                         rx_data有效,表示接收到1个字节的数据。注意,1个时钟的
                         高电平表示接收到1个字节数据。
    uart_ff0          内部信号    输入进来的uart_rx信号寄存一拍后的信号。
                         该信号的目的是为了做时序同步。
    uart_ff1          内部信号    对uart_ff0寄存一拍后的信号。
                         该信号的目的是为了做时序同步。该信号就是同步化后的,
                         可以使用的信号。 
    uart_ff2          内部信号    对uart_ff1寄存一拍的信号。
                         该信号的目的,是与uart_ff1配合,检测uart_ff1的下降沿。
                         
    flag_add          内部信号    模块处于接收数据状态的指示信号,当其为1时,表示正在
                         接收数据;为0时,表示空闲状态。
                         产生逻辑是:当检测到uart_ff1的下降沿时变高,当接收完整
                         个字节数据时(计数器cnt1数完了)就变低。
    cnt0            内部信号   对每输入1bit数据的时间进行计数,接收1bit需要5208个时钟
                         周期。该计数器的计数周期为5208。
    add_cnt0          内部信号   计数器cnt0计数有效信号。当处于处于接收状态时,该信号
                         有效。
    end_cnt0          内部信号   计数器cnt0的结束条件。接收1bit需要5208个时钟周期,所以
                         数到5208个就结束。
    cnt1            内部信号   对接收多少bit的数据进行计数,停止位不参与,起始位加上
                         数据位共9bit。该计数器的计数周期为9。
    add_cnt1           内部信号   计数器cnt1的加一条件。接收完1位(end_cnt0),就有效。
    end_cnt1          内部信号   计数器cnt1的结束条件,共接收9bit数据,所以数到9个就结
                         束。
    add_en           内部信号   uart_ff1下降沿有效指示信号。
                         当检测到接收的数据前一时刻uart_ff2为高电平,
                         当前时刻uart_ff1为低电平时,就有效。

    三、参考代码

     下面展出本模块的设计,欢迎进一步交流,如果需要整个项目源代码,欢迎与明德扬联系。
    

    1       module uart_rx(
    2          clk ,
    3          rst_n ,
    4          uart_rx ,
    5          rx_vld ,
    6          rx_data
    7          );
    8       [size=9.0000pt]
    9
    10         parameter CNT_BTL = 20’d2604;
    11         parameter CNT_MID = 20’d1302;
    12         parameter CNT_RX = 4’d9;
    13         parameter DATA_W = 8;
    14      [size=9.0000pt]
    15         input clk ;
    16         input rst_n ;
    17         input uart_rx ;
    18      [size=9.0000pt]
    19         wire uart_rx ;
    20         output[DATA_W-1:0] rx_data ;
    21         output rx_vld ;
    22
    23      [size=9.0000pt]
    24         reg [DATA_W-1:0] rx_data ;
    25         reg rx_vld ;
    26      [size=9.0000pt]
    27         reg uart_rx_ff0 ;
    28         reg uart_rx_ff1 ;
    29         reg uart_rx_ff2 ;
    30         reg flag_add_add ;
    31         reg [19:0] cnt0 ;
    32         wire add_cnt0 ;
    33         wire end_cnt0 ;
    34      [size=9.0000pt]
    35         reg [3:0] cnt1 ;
    36         wire add_cnt1 ;
    37         wire end_cnt1 ;
    38         wire add_en ;
    39      [size=9.0000pt]
    40         always @(posedge clk or negedge rst_n)begin
    41           if(!rst_n)begin
    42             cnt0 <= 0;
    43           end
    44           else if(add_cnt0)begin
    45             if(end_cnt0)
    46               cnt0 <= 0;
    47             else
    48               cnt0 <= cnt0 + 1;
    49           end
    50         end
    51      [size=9.0000pt]
    52         assign add_cnt0 = flag_add[size=9.0000pt];
    53         assign end_cnt0 = add_cnt0 && cnt0== CNT_BTL-1;
    54      [size=9.0000pt]
    55         always @(posedge clk or negedge rst_n)begin
    56           if(!rst_n)begin
    57             cnt1 <= 0;
    58           end
    59           else if(add_cnt1)begin
    60             if(end_cnt1)
    61               cnt1 <= 0;
    62             else
    63               cnt1 <= cnt1 + 1;
    64           end
    65         end
    66      [size=9.0000pt]
    67        assign add_cnt1 = end_cnt0;
    68        assign end_cnt1 = add_cnt1 && cnt1== CNT_RX-1;
    69      [size=9.0000pt]
    70        always @(posedge clk or negedge rst_n)begin
    71          if(rst_n1’b0)begin
    72            uart_rx_ff0 <= 1’b1;
    73            uart_rx_ff1 <= 1’b1;
    74            uart_rx_ff2 <= 1’b1;
    75          end
    76          else begin
    77            uart_rx_ff0 <= uart_rx;
    78            uart_rx_ff1 <= uart_rx_ff0;
    79            uart_rx_ff2 <= uart_rx_ff1;
    80          end
    81        end
    82        assign add_en = uart_rx_ff2&&~uart_rx_ff1;
    83
    84        always @(posedge clk or negedge rst_n)begin
    85          if(rst_n
    1’b0)begin
    86            flag_add <= 1’b0;
    87          end
    88          else if(add_en)begin
    89            flag_add <= 1’b1;
    90          end
    91          else if(end_cnt1)begin
    92            flag_add <= 1’b0;
    93          end
    94        end
    95
    96        always @(posedge clk or negedge rst_n)begin
    97          if(rst_n1’b0)begin
    98            rx_vld <= 1’b0;
    99          end
    100         else if(end_cnt1)begin
    101           rx_vld <= 1’b1;
    102         end
    103         else begin
    104           rx_vld <= 1’b0;
    105         end
    106       end
    107
    108       always @(posedge clk or negedge rst_n)begin
    109         if(rst_n
    1’b0)begin
    110           rx_data <= 8’b0;
    111         end
    112         else if(cnt1!=0&&add_cnt0&&cnt0==CNT_MID-1)begin
    113           rx_data[cnt1-1] <= uart_rx_r2;
    114         end
    115       end
    116
    117     endmodule

       以上就是基于FPGA的边缘检测工程串口接收模块的代码分享,关注明德扬获取边缘检测工程完整的源代码!
    
    展开全文
  • 串口传输代码

    2017-12-15 16:13:32
    串口传输数据解析代码 。 包括封包 解包 。 面向对象 #include "transmission.h" static unsigned char check_sum(const unsigned char * buff, unsigned char len) { unsigned char num = 0; while (len--)...

    串口传输数据解析代码 。

    包括封包 解包 。  面向对象



    #ifndef TRANSMISSION_H
    #define TRANSMISSION_H
    
    #define PACK_DATA_HEAD   0xFF
    #define PACK_DATA_TAIL   0xFE
    #define PACK_DATA_MASK   0x55
    
    #define TRANSMISSION_CACHE_LEN  100
    
    
    struct transmission
    {
    	void(*device_send_buff)(unsigned char * buff, unsigned char len);
    	void(*device_send_byte)(unsigned char byte);
    	void(*user_recv_buff)(unsigned char * buff, unsigned char len);
    	unsigned char pack_recv_cache_buff[TRANSMISSION_CACHE_LEN]; 
    	int pack_recv_pointer;
    	unsigned char pack_recv_last_data;
    	unsigned char flag_recv_star;
    	unsigned char flag_recv_end;
    };
    
    
    void transmit_send_pack_buff(struct transmission *trans,const unsigned char * buff, unsigned char len);
    void transmit_send_pack_byte(struct transmission* trans,const unsigned char byte);
    void transmit_recv_unpack_buff(struct transmission *trans,const unsigned char * buff, unsigned char len);
    void transmit_recv_unpack_byte(struct transmission *trans,const unsigned char byte);
    
    
    
    #endif
    
    
    
    
    
    
    

















    #include "transmission.h"
    
    
    
    
    static unsigned char check_sum(const unsigned char * buff, unsigned char len)
    {
    	unsigned char num = 0;
    	while (len--)
    	{
    		num += *(buff++);
    	}
    	return num;
    }
    
    void transmit_send_pack_buff(struct transmission *trans ,const unsigned char * buff, unsigned char len)
    {
    	unsigned char cache[TRANSMISSION_CACHE_LEN] = { PACK_DATA_HEAD, PACK_DATA_HEAD };
    	unsigned char idx = 2;
    	unsigned char checkSum = check_sum(buff, len);
    
    	while (len--)
    	{
    		cache[idx++] = *buff;
    
    		if (*buff == PACK_DATA_HEAD || *buff == PACK_DATA_TAIL)
    		{
    			cache[idx++] = PACK_DATA_MASK;
    		}
    
    		buff++;
    	}
    
    	cache[idx++] = checkSum;
    	cache[idx++] = PACK_DATA_TAIL;
    	cache[idx++] = PACK_DATA_TAIL;
    
    	if (trans->device_send_buff != 0)
    	{
    		trans->device_send_buff(cache, idx);
    	}
    	else
    	{
    		while (len < idx)
    		{
    			trans->device_send_byte(cache[len++]);
    		}
    	}
    
    }
    
    
    void transmit_send_pack_byte(struct transmission* trans ,const unsigned char byte)
    {
    	unsigned char buff[1] = { byte };
    	transmit_send_pack_buff(trans ,buff, 1);
    }
    
    
    void transmit_recv_unpack_buff( struct transmission *trans ,const unsigned char * buff, unsigned char len)
    {
    	while (len--)
    	{
    		if (*buff == PACK_DATA_HEAD && trans->pack_recv_last_data == PACK_DATA_HEAD)
    		{
    			trans->flag_recv_star = 1;
    			trans->pack_recv_pointer = 0;
    			trans->flag_recv_end = 0;
    		}
    		else if (*buff == PACK_DATA_MASK && (trans->pack_recv_last_data == PACK_DATA_HEAD || trans->pack_recv_last_data == PACK_DATA_TAIL) && trans->pack_recv_pointer > 0)
    		{
    			//do nothing
    		}
    		else if (*buff == PACK_DATA_TAIL && trans->pack_recv_last_data == PACK_DATA_TAIL)
    		{
    			if (trans->flag_recv_star)
    			{
    				if (trans->pack_recv_pointer > 2)
    				{
    					if (trans->pack_recv_cache_buff[trans->pack_recv_pointer - 2] == check_sum(trans->pack_recv_cache_buff, trans->pack_recv_pointer - 2))
    					{
    						//receive success
    						trans->user_recv_buff(trans->pack_recv_cache_buff, trans->pack_recv_pointer - 2);
    					}
    				}
    
    			}
    			trans->flag_recv_star = 0;
    			trans->pack_recv_pointer = 0;
    		}
    		else
    		{
    			if (trans->flag_recv_star)
    			{
    				if(trans->pack_recv_pointer<TRANSMISSION_CACHE_LEN) 
    				trans->pack_recv_cache_buff[trans->pack_recv_pointer++] = *buff;
    			}
    		}
    
    		trans->pack_recv_last_data = *buff;
    		buff++;
    	}
    }
    
    
    
    void transmit_recv_unpack_byte(struct transmission *trans ,const unsigned char byte)
    {
    
    	if (byte == PACK_DATA_HEAD && trans->pack_recv_last_data == PACK_DATA_HEAD)
    	{
    		trans->flag_recv_star = 1;
    		trans->pack_recv_pointer = 0;
    		trans->flag_recv_end = 0;
    	}
    	else if (byte == PACK_DATA_MASK && (trans->pack_recv_last_data == PACK_DATA_HEAD || trans->pack_recv_last_data == PACK_DATA_TAIL) && trans->pack_recv_pointer > 0)
    	{
    		//do nothing
    	}
    	else if (byte == PACK_DATA_TAIL && trans->pack_recv_last_data == PACK_DATA_TAIL)
    	{
    		if (trans->flag_recv_star)
    		{
    			if (trans->pack_recv_pointer > 2)
    			{
    				if (trans->pack_recv_cache_buff[trans->pack_recv_pointer - 2] == check_sum(trans->pack_recv_cache_buff, trans->pack_recv_pointer - 2))
    				{
    					//receive success
    					trans->user_recv_buff(trans->pack_recv_cache_buff, trans->pack_recv_pointer - 2);
    				}
    			}
    		}
    		trans->flag_recv_star = 0;
    		trans->pack_recv_pointer = 0;
    	}
    	else
    	{
    		if (trans->flag_recv_star)
    		{
    			if(trans->pack_recv_pointer<TRANSMISSION_CACHE_LEN)
    			trans->pack_recv_cache_buff[trans->pack_recv_pointer++] = byte;
    		}
    	}
    	trans->pack_recv_last_data = byte;
    }
    
    
    


    
    

    
    

    展开全文
  • } /* ****************************************************************** * 名称: uart_recv_data_frame * 功能: 串口数据每帧数据解析 * 入口参数: * 出口参数: 正确返回为0,错误返回为-1 ***************...
    /*
    ********************************************************************************
    
    *******************************************************************************
    */
    
    #include<stdio.h>      /*标准输入输出定义*/    
    #include<stdlib.h>     /*标准函数库定义*/    
    #include<unistd.h>     /*Unix 标准函数定义*/    
    #include<sys/types.h>     
    #include<sys/stat.h>       
    #include<fcntl.h>      /*文件控制定义*/    
    #include<termios.h>    /*PPSIX 终端控制定义*/    
    #include<errno.h>      /*错误号定义*/    
    #include<string.h>
    #include<pthread.h>
    
    #define RET_ERR  -1    
    #define RET_OK   0
    
    #define LEN_BUF_RECV_UNPACK 16
    #define LEN_BUF_RECV 64
    
    
    static int g_fd;
    static char s_buf_recv[LEN_BUF_RECV_UNPACK];
    
    /*
    ******************************************************************  
    *名称:             uart_open  
    *功能:             打开串口并返回串口设备文件描述  
    *入口参数:         fd      文件描述符
                        dev    串口号(ttyS0,ttyS1,ttyS2)  
    *出口参数:正确返回为0,错误返回为-1  
    ******************************************************************
    */    
    int uart_open(char*dev, int *fd)
    {    
    	int ret;
    	
    	*fd = open(dev, O_RDWR|O_NOCTTY|O_NDELAY);  
    	printf("fd->open=%d\n", *fd);   
    	if (0 > *fd) {    
    		printf("Can't Open Serial Port");    
    		return RET_ERR;    
    	}    
    	//恢复串口为阻塞状态
    	ret = fcntl(*fd, F_SETFL, 0);  
    	printf("fcntl=%d\n", ret);                                  
    	if(0 > ret) {    
    		printf("fcntl failed!\n");    
    		return RET_ERR;    
    	}  
    	//测试是否为终端设备        
    	if(0 == isatty(STDIN_FILENO)) {    
    		printf("standard input is not a terminal device\n");    
    		return RET_ERR;    
    	}     
    	printf("isatty success!\n");    
    	    
    	
    	return RET_OK;    
    }    
    
    
    
    /*
    ******************************************************************  
    *名称:             uart_close  
    *功能:             关闭串口并返回串口设备文件描述  
    *入口参数:         fd          文件描述符   
                        port        串口号(ttyS0,ttyS1,ttyS2)  
    *出口参数:void  
    ******************************************************************
    */         
    void uart_close(int fd)    
    {    
        close(fd);    
    }    
         
         
         
    /*
    ******************************************************************  
    *名称:             uart_set  
    *功能:             设置串口数据位,停止位和效验位  
    *入口参数:         fd          串口文件描述符
    *                   speed       串口速度  
    *                   flow_ctrl   数据流控制  
    *                   databits    数据位   取值为 7 或者8  
    *                   stopbits    停止位   取值为 1 或者2  
    *                   parity      效验类型 取值为N,E,O,,S  
    *出口参数:正确返回为0,错误返回为-1  
    ******************************************************************
    */    
    int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)    
    {      
    	int i, status;    
    	int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};    
    	int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};    
    	
    	struct termios options;    
    	
    	/*  tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,
    	该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.  */    
    	if(0 != tcgetattr(fd, &options)) {    
    		printf("SetupSerial 1\n");        
    		return RET_ERR;     
    	}    
    	
    	//设置串口输入波特率和输出波特率    
    	for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {    
    		if  (speed == name_arr[i]) {                 
    			cfsetispeed(&options, speed_arr[i]);     
    			cfsetospeed(&options, speed_arr[i]);      
    		}    
    	}         
    	
    	//修改控制模式,保证程序不会占用串口    
    	options.c_cflag |= CLOCAL;    
    	//修改控制模式,使得能够从串口中读取输入数据    
    	options.c_cflag |= CREAD;    
    	
    	//设置数据流控制    
    	switch(flow_ctrl) {              
    	case 0://不使用流控制    
    		options.c_cflag &= ~CRTSCTS;    
    		break;      	
    	case 1://使用硬件流控制    
    		options.c_cflag |= CRTSCTS;    
    		break;    
    	case 2://使用软件流控制    
    		options.c_cflag |= IXON | IXOFF | IXANY;    
    		break;    
    	}    
    	//设置数据位    
    	//屏蔽其他标志位    
    	options.c_cflag &= ~CSIZE;    
    	switch (databits) {      
    	case 5:    
    		options.c_cflag |= CS5;    
    		break;    
    	case 6:    
    		options.c_cflag |= CS6;    
    		break;    
    	case 7:        
    		options.c_cflag |= CS7;    
    		break;    
    	case 8:        
    		options.c_cflag |= CS8;    
    		break;      
    	default:       
    		printf("Unsupported data size\n");    
    		return RET_ERR;     
    	}    
    	//设置校验位    
    	switch (parity) {      
    	case 'n':    
    	case 'N': //无奇偶校验位。    
    		options.c_cflag &= ~PARENB;     
    		options.c_iflag &= ~INPCK;        
    		break;     
    	case 'o':      
    	case 'O'://设置为奇校验        
    		options.c_cflag |= (PARODD | PARENB);     
    		options.c_iflag |= INPCK;                 
    		break;     
    	case 'e':     
    	case 'E'://设置为偶校验      
    		options.c_cflag |= PARENB;           
    		options.c_cflag &= ~PARODD;           
    		options.c_iflag |= INPCK;          
    		break;    
    	case 's':    
    	case 'S': //设置为空格     
    		options.c_cflag &= ~PARENB;    
    		options.c_cflag &= ~CSTOPB;    
    		break;     
    	default:      
    		printf("Unsupported parity\n");        
    		return RET_ERR;     
    	}     
    	// 设置停止位     
    	switch (stopbits) {      
    	case 1:       
    		options.c_cflag &= ~CSTOPB; 
    		break;     
    	case 2:       
    		options.c_cflag |= CSTOPB; 
    		break;    
    	default:       
    	       printf("Unsupported stop bits\n");     
    	       return RET_ERR;    
    	}    
    	
    	//修改输出模式,原始数据输出    
    	options.c_oflag &= ~OPOST;    
    	
    	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
    	//options.c_lflag &= ~(ISIG | ICANON);    
    	options.c_iflag &= ~ICRNL; //禁止将输入的CR转换为NL
    	options.c_iflag &= ~(IXON); //清bit位 关闭流控字符
    	//设置等待时间和最小接收字符    
    	options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */      
    	options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */    
    	
    	//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读    
    	tcflush(fd,TCIFLUSH);    
    	
    	//激活配置 (将修改后的termios数据设置到串口中)    
    	if (0 != tcsetattr(fd,TCSANOW,&options)) {    
    		printf("com set error!\n");      
    		return RET_ERR;     
    	}   
    	
    	return RET_OK;     
    }    
    
    
    
    /*
    ******************************************************************  
    *名称:                uart_init()  
    *功能:                串口初始化  
    *入口参数:            fd         文件描述符    
    *                      speed      串口速度  
    *                      flow_ctrl  数据流控制  
    *                      databits   数据位   取值为 7 或者8  
    *                      stopbits   停止位   取值为 1 或者2  
    *                      parity     效验类型 取值为N,E,O,,S  
    *                        
    *出口参数:正确返回为0,错误返回为-1   
    ******************************************************************
    */    
    int uart_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)    
    {    
    	int ret;    
    	//设置串口数据帧格式    
    	//ret = uart_set(fd, 115200, 0, 8, 1, 'N');                                                             
    	ret = uart_set(fd, speed, flow_ctrl, databits, stopbits, parity);     
    	return  ret;   
            
    }    
         
       
       
    /*
    *******************************************************************  
    * 名称:            uart_send  
    * 功能:            发送数据  
    * 入口参数:        fd           文件描述符      
    *                   buf     存放串口发送数据  
    *                   data_len     一帧数据的个数  
    * 出口参数:        正确返回为0,错误返回为-1  
    ******************************************************************
    */    
    int uart_send(int fd, char *buf, int data_len)    
    {    
    	int len = 0;    
    	
    	len = write(fd, buf, data_len);    
    	printf("send data is %s\n", buf);
    	if (len != data_len) {                     
    		tcflush(fd, TCOFLUSH);    
    		return RET_ERR;    
    	}    
    	
    	return RET_OK;
    }    
    
    /*
    ******************************************************************  
    * 名称:            uart_unpack  
    * 功能:            串口数据解包  
    * 入口参数:        c         串口数据    
    *                   
    * 出口参数:        正确返回为0,错误返回为-1    
    ******************************************************************
    */    
    static int uart_unpack(unsigned char c)
    {
    	//printf("%s is running\r\n",__FUNCTION__);
    	static int flg, index;
    	
    	/*接收到的数据开头为A*/
    	if('A' == c) {
    		flg = 1;
    		index = 0;
    	} else if('\n' == c && 0 < index && '\r' == s_buf_recv[index - 1]) { /*以回车换行结束如:A123456\r\n*/		
    		//buf[index] = c;
    		index = 0;
    		flg = 0;
    		return RET_OK;
    	}
    	/*没找到继续找*/
    	if (1 == flg) {
    		s_buf_recv[index++] = c;		
    		if(index >= sizeof(s_buf_recv))
    			index = 0;
    	}
    	
    	return RET_ERR;
    }
    /*
    ******************************************************************  
    * 名称:            uart_recv_data_frame  
    * 功能:            串口数据每帧数据解析  
    * 入口参数:         
    * 出口参数:        正确返回为0,错误返回为-1    
    ******************************************************************
    */    
    static int uart_recv_data_frame()
    {
    	int len;
    	unsigned char type;
    	
    	printf("buf0-7 %x %x %x %x  %x %x %x %x", s_buf_recv[0], s_buf_recv[1],s_buf_recv[2],s_buf_recv[3],s_buf_recv[4],s_buf_recv[5],s_buf_recv[6],s_buf_recv[7]);
    	len = s_buf_recv[1];
    	if ('A' != s_buf_recv[0] || 0 >= len || sizeof(s_buf_recv) <= len + 2 || 0x0d != s_buf_recv[len + 2]) {
    		printf("unknow uart_recv_data_frame:%d\n", len);
    		return RET_ERR;		
    	}
    	type = (unsigned char)s_buf_recv[2];
    	switch(type){
    	case 0xAA: 		  			            	
            	printf("uart_recv_data_frame:0xaa\n");	
            	break;    
    	default:	
            	printf("unknow uart_recv_data_frame:%x\n", type);		
            	break;			
    	}
    	
    	return RET_OK;
    }
    /*
    ******************************************************************  
    * 名称:            uart_recv_thread  
    * 功能:            接收串口数据线程  
    * 入口参数:         
    * 出口参数:        错误返回为-1    
    ******************************************************************
    */  
    static void *uart_recv_thread(void *arg)
    {
    	int ret, i, n;
    	fd_set readfds;
    	char buf[LEN_BUF_RECV];
    	
    	while(1) {
    		FD_ZERO(&readfds);
    		FD_SET(g_fd, &readfds);
    		
    		
    		ret = select(g_fd + 1, &readfds, NULL, NULL, NULL);
    		//memset(buf, 0, sizeof(buf));
    		
    		if(0 >= ret) {
    			printf("select failed %d\n", ret); //0超时 1失败
    			continue;
    		} 
    		if(0 >= FD_ISSET(g_fd, &readfds)){
    			printf("FD_ISSET failed\n"); 
    			continue;
    		} 
    		do {
    			n = read(g_fd, buf, LEN_BUF_RECV);	//配置为非阻塞
    			if(0 >= n) {
    				printf("read failed %d\n", n); 
    				//conitnue;
    				break;
    			}	
    			for (i = 0; i < n; i++) {
    				ret = uart_unpack(buf[i]);
    				if (RET_OK == ret) {
    					uart_recv_data_frame();	
    				}
    			}
    		}while(0 < n);
    	}
    	
    	return NULL;
    }
    
    /*
    ********************************************************************************
    
    *******************************************************************************
     */
     
     
    int main(int argc, char **argv)    
    {
    	int ret, len, i;            
    	
    	char rcv_buf[256];             
    	char send_buf[256];
    	pthread_t p_serial;
    	
    	
    	if(argc != 2) {    
    		printf("Usage: %s /dev/ttySn 0      #(send data)\n",argv[0]);
    		printf("Usage: %s /dev/ttySn 1      #1(receive data)\n",argv[1]);
    		printf("open failure : %s\n", strerror(errno));
    		
    		return RET_ERR;    
    	}    
    	ret = uart_open(argv[1], &g_fd); //打开串口,返回文件描述符   
    	// fd=open("dev/ttyS1", O_RDWR);
    	//printf("fd= \n",fd);
    	
    	ret = uart_init(g_fd, 9600, 0, 8, 1, 'N');    
    	printf("Set Port Exactly!\n"); 
    	//sleep(1);   
    	if(RET_ERR == ret) {
    		printf("ret init = %d\n",ret);
    		return RET_ERR; 
    	}    
    	pthread_create(&p_serial, NULL, uart_recv_thread, NULL);/*线程函数在下面*/
    	
    	
    	fgets(send_buf, 256, stdin);   //输入内容,最大不超过40字节,fgets能吸收回车符,这样pc收到的数据就能自动换行     
    	for(i = 0; i < 10; i++) {    
    		len = uart_send(g_fd, send_buf, 40);    
    		if(len > 0)    
    			printf(" %d time send %d data successful\n",i,len);    
    		else    
    			printf("send data failed!\n");    
    		              
    		sleep(1);    
    	}    
    	uart_close(g_fd);                 
         
    }    
    

     

    展开全文
  • 串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。 注:串口波特率9600,无奇偶校验位。一、设计架构上图是与上位机通信的串口的时序图。我们从图中可以获取到如下关键信息。...

    本文为明德扬原创文章,转载请注明出处!

    串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。

    注:串口波特率9600,无奇偶校验位。

    一、设计架构

    a9572f9ae5fe1eb89560d2a76bebde0d.png

    上图是与上位机通信的串口的时序图。我们从图中可以获取到如下关键信息。

    1. 串口数据线位宽为1bit,默认状态下为高电平。

    2. 每次上游模块发送数据,都是先发送1位的起始位0,然后发送8位的数据,最后是1位的停止位1。

    3. 每1位所占的时间,可以通过波特率来计算。计算方法如下:

    波特率是指1s内发送或接受了多少比特数据,若波特率为9600 bit/s,则1s可以传输9600bit,那么传输1bit的时间为:1/9600(s)。以mp801开发板为例,时钟频率为50M,那么发送1bit就需要5208个时钟周期。

    串口接收模块采用两个计数器的架构,这两个计数器分别表示接收1bit需要的时间和共需要接收多少bit。其结构图如下:

    8fe8835f1b206ff54ac9f700af9edef0.png

    计数器cnt0:对接收1bit数据需要的时间进行计数;接收1bit需要5208个时钟周期。该计数器的计数周期为5208。

    计数器cnt1:对接收多少bit的数据进行计数;停止位不参与,起始位加上数据位共9bit。该计数器的计数周期为9。

    本工程使用了检测信号下降沿的方法,信号下降沿的检测方法:

    检查uart_rx的下降沿,就要用到FPGA里的边沿检测技术。所谓的边沿检测,就是检测输入信号,或者FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测。就比如前面uart_rx由1变0时,就出现了下降沿,接着一次指令结束,uart_rx由0变1时,就出现了上升沿,边沿检测技术这在FPGA电路设计泛。电路图如下:

    7521adec266cd5adb1f177486f6bd1d3.png

    对应的信号列表如下图所示:

    b2085aa9c4b8d3cb7a6401e2d9618281.png

    中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。

    我们来讲解这个原理,信号的波形图如下:

    8c1ee031e8f2d7472824156a0592bfa0.png

    Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,但是相差了一个时钟周期。我们也可以理解为:每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigger之前的值。

    我们在看第3时钟上升沿,此时trigger值为0,而tri_ff0的值为1,即当前trigger的值为0,之前的值为1,这就是下降沿,此时neg_edge为1。当看到neg_edge为1,就表示检测到trigger的下降沿了。同理在第7个时钟上升沿,看到trigger值为1,而之前值为0,pos_edge为1,表示检测到trigger的上升沿。

    本模块输入信号uart_rx是异步信号,异步信号都需要经验同步化后,才能够使用。异步信号同步化如下。

    在前面讨论边沿检测的波形中,我们把trigger当做理想的同步信号来考虑,也就是trigger满足D触发器的建立和保持时间,在同步系统中实现边沿检测不是问题。但如果trigger不是理想的同步信号,例如外部按键信号,以及本工程的uart_rx信号。这些信号什么时候产生变化,是外部传输指令给FPGA,对于FPGA来说完全是随机的。很有可能出现,信号在时钟上升沿产生变化的情况,从而无法满足触发器的建立时间和保持时间要求,出现亚稳态的情况,从而导致系统崩溃。如下图,我们先将信号用2个触发器进行了寄存,确定了信号的稳定性,然后再进行边沿检测,达成了同步系统中实现边沿检测的需求。

    fda0bb6ecb2dfc0368cb979c1b96386f.png

    那么边沿检测的代码设计就需要先进行触发器的设计,假设输入的信号trigger不是同步信号,要将该信号用2个触发器进行寄存,得到tri_ff0和tri_ff1。需要特别注意的是,在第一个触发器阶段,信号依旧有亚稳态的情况,因此tri_ff0绝对不可以拿来当条件使用,只能使用tri_ff1。接着进行检测边沿,根据前面所说,得出同步信号后再用寄存器寄存,得到tri_ff2。根据tri_ff1和tri_ff2,我们就可以得到边沿检测结果。当tri_ff1==1且tri_ff2==0时,上升沿的pos_edge有效;当tri_ff1==0且tri_ff2==1时,下降沿的neg_edge有效,代码如下:

    63e2991374adef8849c4bc771c36716f.png

    综上所述,如果进来的信号是异步信号,那么就需要先进行同步化,再做检测,即通过打两拍的方式,实现了信号的同步化;再通过打一拍的方式,实现边沿检测电路。反之,如果进来的信号本身就是同步信号,那就没有必要做同步化了,可以直接做边沿检测。

    二、信号的意义

    efe2a0fda7cdc148d21e98354b3cdc29.png

    三、参考代码

    下面展出本模块的设计,欢迎进一步交流,如果需要整个项目源代码,欢迎与明德扬联系。
    module uart_rx(

    clk ,

    rst_n ,

    uart_rx ,

    rx_vld ,

    rx_data

    );

    parameter CNT_BTL = 20'd2604;

    parameter CNT_MID = 20'd1302;

    parameter CNT_RX = 4'd9;

    parameter DATA_W = 8;

    input clk ;

    input rst_n ;

    input uart_rx ;

    wire uart_rx ;

    output[DATA_W-1:0] rx_data ;

    output rx_vld ;

    reg [DATA_W-1:0] rx_data ;

    reg rx_vld ;

    reg uart_rx_ff0 ;

    reg uart_rx_ff1 ;

    reg uart_rx_ff2 ;

    reg flag_add_add ;

    reg [19:0] cnt0 ;

    wire add_cnt0 ;

    wire end_cnt0 ;

    reg [3:0] cnt1 ;

    wire add_cnt1 ;

    wire end_cnt1 ;

    wire add_en ;

    always @(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

    cnt0 <= 0;

    end

    else if(add_cnt0)begin

    if(end_cnt0)

    cnt0 <= 0;

    else

    cnt0 <= cnt0 + 1;

    end

    end

    assign add_cnt0 = flag_add[size=9.0000pt];

    assign end_cnt0 = add_cnt0 && cnt0== CNT_BTL-1;

    always @(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

    cnt1 <= 0;

    end

    else if(add_cnt1)begin

    if(end_cnt1)

    cnt1 <= 0;

    else

    cnt1 <= cnt1 + 1;

    end

    end

    assign add_cnt1 = end_cnt0;

    assign end_cnt1 = add_cnt1 && cnt1== CNT_RX-1;

    always @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

    uart_rx_ff0 <= 1'b1;

    uart_rx_ff1 <= 1'b1;

    uart_rx_ff2 <= 1'b1;

    end

    else begin

    uart_rx_ff0 <= uart_rx;

    uart_rx_ff1 <= uart_rx_ff0;

    uart_rx_ff2 <= uart_rx_ff1;

    end

    end

    assign add_en = uart_rx_ff2&&~uart_rx_ff1;

    always @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

    flag_add <= 1'b0;

    end

    else if(add_en)begin

    flag_add <= 1'b1;

    end

    else if(end_cnt1)begin

    flag_add <= 1'b0;

    end

    end

    always @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

    rx_vld <= 1'b0;

    end

    else if(end_cnt1)begin

    rx_vld <= 1'b1;

    end

    else begin

    rx_vld <= 1'b0;

    end

    end

    always @(posedge clk or negedge rst_n)begin

    if(rst_n==1'b0)begin

    rx_data <= 8'b0;

    end

    else if(cnt1!=0&&add_cnt0&&cnt0==CNT_MID-1)begin

    rx_data[cnt1-1] <= uart_rx_r2;

    end

    end

    endmodule

    展开全文
  • 一段代码中的截取,不是很了解这个的具体意思,希望有人能够帮忙代码解析一下,每行代码的作用是什么? #define FOSC 11059200L /// < 晶振 #define BAUD 9600 /// < 波特率 void init_uart1() { ...
  • GRBL二:串口控制命令及代码解析

    万次阅读 多人点赞 2015-06-30 15:59:49
    GRBL二:串口控制命令及参数解析 单片机串口跟电脑连接,测试GRBL串口命令 1.输入:“$”显示如下: $$ (view Grbl settings) //命令“$$”查看GRBL设置 $x=value (save Grbl setting) //保存设置 $H (run ...
  • Linux串口测试工具简单程序代码解析

    千次阅读 2013-05-13 16:59:00
    Linux串口测试工具简单程序代码解析 2010-05-28 10:53 佚名 我要评论(0) 字号:T |T 网上常见的版本都看起来比较烦琐,今天介绍是一个简单一点的,和大家一起学习了解,以下是程序的具体代码 AD:2013...
  • 安卓串口通讯源代码,实现了数据解析,1秒钟定时更新,用于疫情期间温度传感器读取体温,完整的android studio 3.6.1项目,可以直接编译使用
  • 一、uboot命令定义分析 1.1、.u_boot_cmd段 ...在u-boot.lds文件中有如下一段代码: __u_boot_cmd_start = .;//.表示当前地址 .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; 这段代码
  • 通过C#实现串口通信,里面的代码展示了如何监听计算机中的串口解析数据或者发送数据,做串口开发的可以参考
  • 因初学GRBL,网上搜集了一些GRBL的资料,怕遗忘,所以转载过来,如有侵权请联系,...单片机串口跟电脑连接,测试GRBL串口命令 1.输入:“$”显示如下: $$(view Grbl settings) //命令“$$”查看GRBL设置 $x=v...
  • 10X串口协议解析

    2019-01-18 11:28:02
    10X串口协议解析 1、串口协议解析 首先所有命令都要带传感器地址,一般传感器的地址都是1,所以如果没有修改修改传感器地址的话,那么命令的前面都是01(16进制),最大地址是127,即是7F。 常用命令 1、获取传感器...
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...
  • 自定义串口协议解析

    2020-07-22 13:21:01
    直接上代码 if (count == 0) { while (getUartData() != (bs[0] & 0xFF)) ; count = 1; } else if (count == 1) { read = getUartData(); if (read == (bs[0] & 0xFF)) count = 1; if (read == (bs...
  • 第一阶段 串口驱动没有初始化前的准备工作,包括设备树的解析,platform设备注册 asmlinkage void __init start_kernel(void)  =&gt;setup_arch  =&gt;find_legacy_serial_ports();  =&gt;path = of...
  • 写单片机程序大多芯片都会用到串口进行抓取信息、调试代码。那如何编写串口代码呢,各种芯片串口都大同小异,一般会用到寄存器的串口中断使能位、设置波特率位、接受中断标志位、发送中断标志位、缓存寄存器(用于...
  • 每一个包都加一个包头,里面包括数据类型(图片/温湿度),节点号(哪一个模块的数据),数据包的序号,数据包有效长度等信息,辅助解析串口数据。每一个包分两两部分读取,包头+有效数据。从包头获取有效数据长度,...
  • Marlin固件串口功能解析和程序移植

    千次阅读 2019-09-22 09:55:51
    原版Marlin固件硬件平台基于arduino,采用C++类对串口操作函数函数进行了封装,代码注释中介绍了这些函数的功能。 MarlinSerial.h文件中类的定义,此处的类只保留的框架结构,留存的这些函数基本上是要一直到STM32...
  • DSP串口烧录过程解析

    千次阅读 2017-08-11 17:38:31
    实现串口烧写技术软件分为PC机内用于数据发送的顶层软件和目标机内的底层软件。...若需要,则将串口发送的目标代码烧写至F2812片内Flash指定扇区;否则将继续执行原有的用户目标代码。烧录过程主要
  • @RequiresApi(api = Build.VERSION_CODES.N) @Override public void onNewData(byte[] data) { /** * 例如:01 03 12 34 00 01 C0 BC * 01 03为固定数据 * 12 34为编号 * 00 01为数据,最大值FF FF(十进制...
  • 本文介绍设计一个环形队列数据结构以实现串口更稳定的接收消息,并有效防止丢包 。 这段时间一直在研究多旋翼飞行器,以及其它的事情,博客好外没更新,再不坚持怕真荒废了哦。 在上篇简单实现MAVLink协议的解析...
  • linux 串口驱动解析之2440

    千次阅读 2016-01-29 14:00:45
    对于串口驱动的移植准备自己分析一下源代码的,但是发现自己好多地方都只知道一些皮毛,不明白其中的道理,所以我上网搜的时候发现有好多人写了很多很好的文章了,下面我转载的这篇就非常不错,一个困恼我好久的问题...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 394
精华内容 157
关键字:

串口解析代码