精华内容
下载资源
问答
  • 1.串口收发流程图 串口的波特率是指每秒收/发的bit数,此代码设为115200bit/s。因此要将50MHz的系统时钟进行适当的分频。如上图,每计到CYCLE个时钟脉冲就传输一个位。因此CYCLE=50_000_000/115200。 下面的代码是...

    一.原理图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    二.串口收发

    1.串口收发流程图

    在这里插入图片描述
    串口的波特率是指每秒收/发的bit数,此代码设为115200bit/s。因此要将50MHz的系统时钟进行适当的分频。如上图,每计到CYCLE个时钟脉冲就传输一个位。因此CYCLE=50_000_000/115200。

    下面的代码是对黑金的代码进行了一些更改和注释。

    2.uart_rx.v

    接收数据状态图:
    在这里插入图片描述
    串口接收模块

    module uart_rx
    #(
    	parameter CLK_FRE = 50,      				   //系统时钟频率(Mhz)
    	parameter BAUD_RATE = 115200 				   //串口波特率,bit/s
    )
    (
    	input                        clk,              //系统时钟输入
    	input                        rst_n,            //复位键
    	output reg[7:0]              rx_data,          //串口接收到的数据输出
    	output reg                   rx_data_valid,    //一个字节接收完成标志
    	input                        rx_data_ready,    //准备接收数据标志
    	input                        rx_pin            //接收引脚,空闲时保持高电平
    );
    
    //计算符合波特率的时钟频率
    localparam                       CYCLE = CLK_FRE * 1000000 / BAUD_RATE;	
    
    localparam                       S_IDLE      = 1;
    localparam                       S_START     = 2;  //开始状态
    localparam                       S_REC_BYTE  = 3;  //接收数据状态
    localparam                       S_STOP      = 4;  //停止状态
    localparam                       S_DATA      = 5;
    
    reg[2:0]                         state;
    reg[2:0]                         next_state;
    reg                              rx_d0;            //delay 1 clock for rx_pin
    reg                              rx_d1;            //delay 1 clock for rx_d0
    wire                             rx_negedge;       //接收引脚下边沿判断
    reg[7:0]                         rx_bits;          //暂存接收到的数据
    reg[15:0]                        cycle_cnt;        //波特率计数器
    reg[2:0]                         bit_cnt;          //位计数器
    
    assign rx_negedge = rx_d1 && ~rx_d0;	//如果为1,说明接收到低电平的开始位
    
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    	begin
    		rx_d0 <= 1'b0;
    		rx_d1 <= 1'b0;	
    	end
    	else
    	begin
    		rx_d0 <= rx_pin;	//非阻塞赋值
    		rx_d1 <= rx_d0;
    	end
    end
    
    //接收数据状态机
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		state <= S_IDLE;
    	else
    		state <= next_state;
    end
    
    always@(*)
    begin
    	case(state)
    		S_IDLE:
    			if(rx_negedge)					//有低电平信号,开始接收
    				next_state <= S_START;
    			else
    				next_state <= S_IDLE;
    		S_START:
    			if(cycle_cnt == CYCLE - 1)		//等待一个CYCLE周期
    				next_state <= S_REC_BYTE;
    			else
    				next_state <= S_START;
    		S_REC_BYTE:					//开始接收
    			if(cycle_cnt == CYCLE - 1  && bit_cnt == 3'd7)  //接收到8位数据后进入停止状态
    				next_state <= S_STOP;
    			else
    				next_state <= S_REC_BYTE;
    		S_STOP:
    			if(cycle_cnt == CYCLE/2 - 1)	//等待半个CYCLE周期,不能太长,防止错过下一次数据接收
    				next_state <= S_DATA;
    			else
    				next_state <= S_STOP;
    		S_DATA:
    			if(rx_data_ready)    //数据接收结束
    				next_state <= S_IDLE;
    			else
    				next_state <= S_DATA;
    		default:
    			next_state <= S_IDLE;
    	endcase
    end
    
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		rx_data_valid <= 1'b0;
    	else if(state == S_STOP && next_state != state)	//可以正常进入S_DATA状态说明接收数据有效
    		rx_data_valid <= 1'b1;	//发出接收有效信号
    	else if(state == S_DATA && rx_data_ready)
    		rx_data_valid <= 1'b0;	//接收有效信号已发出,可以复位
    end
    
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		rx_data <= 8'd0;
    	else if(state == S_STOP && next_state != state)	//可以正常进入S_DATA状态说明接收数据有效
    		rx_data <= rx_bits;	//输出接收到的数据
    end
    
    //波特率相关计数
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		cycle_cnt <= 16'd0;
    	else if((state == S_REC_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)	
    		cycle_cnt <= 16'd0;		//计完一个CYCLE周期或者状态改变则计数复位
    	else
    		cycle_cnt <= cycle_cnt + 16'd1;	
    end
    
    //换下一位接收
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		begin
    			bit_cnt <= 3'd0;
    		end
    	else if(state == S_REC_BYTE)
    		if(cycle_cnt == CYCLE - 1)	//正好一个CYCLE周期
    			bit_cnt <= bit_cnt + 3'd1;	//位数加1(因为先接收数据的低位)
    		else
    			bit_cnt <= bit_cnt;
    	else
    		bit_cnt <= 3'd0;
    end
    
    //接收数据
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		rx_bits <= 8'd0;
    	else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)	//处于接收状态且正好半个CYCLE周期
    		rx_bits[bit_cnt] <= rx_pin;	//接收一位数据
    	else
    		rx_bits <= rx_bits; 
    end
    endmodule 
    

    3.uart_tx.v

    发送与接收类似(更简单一点),此不过多说明。

    module uart_tx
    #(
    	parameter CLK_FRE = 50,      //系统时钟频率(MHz)
    	parameter BAUD_RATE = 115200 //波特率
    )
    (
    	input                        clk,              //系统时钟输入
    	input                        rst_n,            //复位
    	input[7:0]                   tx_data,          //串口要发送数据
    	input                        tx_data_ready,    //准备发送数据标志
    	output reg                   tx_data_valid,    //一个字节发送完成标志
    	output                       tx_pin            //发送引脚
    );
    localparam                       CYCLE = CLK_FRE * 1000000 / BAUD_RATE;
    
    localparam                       S_IDLE       = 1;
    localparam                       S_START      = 2;//开始状态
    localparam                       S_SEND_BYTE  = 3;//发送字节数据状态
    localparam                       S_STOP       = 4;//停止状态
    
    reg[2:0]                         state;
    reg[2:0]                         next_state;
    reg[15:0]                        cycle_cnt; 		 //cycle循环计数器(与波特率有关)
    reg[2:0]                         bit_cnt;			 //当前发送的位号
    reg[7:0]                         tx_data_latch;  //暂存要发送的数据
    reg                              tx_reg; 			 //暂存当前要发送的位
    
    assign tx_pin = tx_reg;	//按位发送数据
    
    //发送数据状态机
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		state <= S_IDLE;
    	else
    		state <= next_state;
    end
    
    always@(*)
    begin
    	case(state)
    		S_IDLE:
    			if(tx_data_ready == 1'b1)
    				next_state <= S_START;
    			else
    				next_state <= S_IDLE;
    		S_START:
    			if(cycle_cnt == CYCLE - 1)
    				next_state <= S_SEND_BYTE;
    			else
    				next_state <= S_START;
    		S_SEND_BYTE:
    			if(cycle_cnt == CYCLE - 1  && bit_cnt == 3'd7)
    				next_state <= S_STOP;
    			else
    				next_state <= S_SEND_BYTE;
    		S_STOP:
    			if(cycle_cnt == CYCLE - 1)
    				next_state <= S_IDLE;
    			else
    				next_state <= S_STOP;
    		default:
    			next_state <= S_IDLE;
    	endcase
    end
    
    //一个字节发送完成信号
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		begin
    			tx_data_valid <= 1'b0;
    		end
    	else if(state == S_IDLE)
    		if(tx_data_ready == 1'b1)
    			tx_data_valid <= 1'b0;
    		else
    			tx_data_valid <= 1'b1;
    	else if(state == S_STOP && cycle_cnt == CYCLE - 1)	//一个字节发送结束
    			tx_data_valid <= 1'b1;	//给出发送有效信号
    end
    
    //把要发送的字节存入寄存器等待发送
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		begin
    			tx_data_latch <= 8'd0;
    		end
    	else if(state == S_IDLE && tx_data_ready == 1'b1)
    			tx_data_latch <= tx_data;
    		
    end
    
    //切换到下一个要发送的位号
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		begin
    			bit_cnt <= 3'd0;
    		end
    	else if(state == S_SEND_BYTE)
    		if(cycle_cnt == CYCLE - 1)		//一个CYCLE周期结束(即一个位发送完)
    			bit_cnt <= bit_cnt + 3'd1;	
    		else
    			bit_cnt <= bit_cnt;
    	else
    		bit_cnt <= 3'd0;
    end
    
    //波特率相关计数
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		cycle_cnt <= 16'd0;
    	else if((state == S_SEND_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)
    		cycle_cnt <= 16'd0;	//计完一个CYCLE周期或者状态改变则计数复位
    	else
    		cycle_cnt <= cycle_cnt + 16'd1;	
    end
    
    //给出当前要发送的位数据
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		tx_reg <= 1'b1;
    	else
    		case(state)
    			S_IDLE,S_STOP:
    				tx_reg <= 1'b1; 	//发送高电平结束位
    			S_START:
    				tx_reg <= 1'b0; 	//发送低电平起始位
    			S_SEND_BYTE:
    				tx_reg <= tx_data_latch[bit_cnt];
    			default:
    				tx_reg <= 1'b1; 
    		endcase
    end
    
    endmodule 
    

    三.主模块

    此实验效果是循环发送“HELLO ALINX"。同时,如果接收到上位机发送的数据,即立刻把接收到的数据再发送出去。

    module uart_test(
    	input                        clk,
    	input                        rst_n,
    	
    	input                        uart_rx,
    	output                       uart_tx
    );
    
    parameter                        CLK_FRE = 50;	//Mhz
    localparam                       IDLE =  0;
    localparam                       SEND =  1;   	//发送HELLO ALINX\r\n状态
    localparam                       WAIT =  2;   	//等待1s然后发送接收到的数据状态
    
    reg[7:0]                         tx_data;			//暂存要发送的字节数据
    reg[7:0]                         tx_str;			
    reg                              tx_data_ready; //准备发送数据标志
    wire                             tx_data_valid;	//一个字节发送完成标志
    reg[7:0]                         tx_cnt;
    wire[7:0]                        rx_data;
    wire                             rx_data_valid;	//一个字节接收完成标志
    wire                             rx_data_ready;	//准备接收数据标志
    reg[31:0]                        wait_cnt;
    reg[3:0]                         state;
    
    assign rx_data_ready = 1'b1;	//接收模式一直保持打开,
    										//如果数据正在发送,则丢弃接收到的数据
    
    //
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    	begin
    		wait_cnt <= 32'd0;
    		tx_data <= 8'd0;
    		state <= IDLE;
    		tx_cnt <= 8'd0;
    		tx_data_ready <= 1'b0;
    	end
    	else
    	case(state)
    		IDLE:
    			state <= SEND;
    		SEND:
    		begin
    			wait_cnt <= 32'd0;
    			tx_data <= tx_str;
    
    			if(tx_data_ready == 1'b1 && tx_data_valid == 1'b1 && tx_cnt < 8'd12)//处于发送状态且未发送完
    			begin
    				tx_cnt <= tx_cnt + 8'd1; //发送的字节计数
    			end
    			else if(tx_data_ready && tx_data_valid)	//最后一字节发送完
    			begin
    				tx_cnt <= 8'd0;
    				tx_data_ready <= 1'b0;
    				state <= WAIT;
    			end
    			else if(~tx_data_ready)	//前一次发送完清零后再一次打开,进行新一轮发送
    			begin
    				tx_data_ready <= 1'b1;
    			end
    		end
    		WAIT:
    		begin
    			wait_cnt <= wait_cnt + 32'd1;
    
    			if(rx_data_valid == 1'b1)	//如果接收到数据则打开发送
    			begin
    				tx_data_ready <= 1'b1;
    				tx_data <= rx_data;   // 发送串口刚接收到的数据
    			end
    			else if(tx_data_ready && tx_data_valid)
    			begin
    				tx_data_ready <= 1'b0;
    			end
    			else if(wait_cnt >= CLK_FRE * 1000000) // 等待一秒再进入下一次发送(1s内均可接收数据)
    				state <= SEND;
    		end
    		default:
    			state <= IDLE;
    	endcase
    end
    
    //需要发送的数据
    always@(*)
    begin
    	case(tx_cnt)	//依据发送字节计数器切换下一个发送的字节
    		8'd0 :  tx_str <= "H";
    		8'd1 :  tx_str <= "E";
    		8'd2 :  tx_str <= "L";
    		8'd3 :  tx_str <= "L";
    		8'd4 :  tx_str <= "O";
    		8'd5 :  tx_str <= " ";
    		8'd6 :  tx_str <= "A";
    		8'd7 :  tx_str <= "L";
    		8'd8 :  tx_str <= "I";
    		8'd9 :  tx_str <= "N";
    		8'd10:  tx_str <= "X";
    		8'd11:  tx_str <= "\r";
    		8'd12:  tx_str <= "\n";
    		default:tx_str <= 8'd0;
    	endcase
    end
    
    //实例化接收模块
    uart_rx#
    (
    	.CLK_FRE(CLK_FRE),
    	.BAUD_RATE(115200)
    ) uart_rx_inst
    (
    	.clk                        (clk                      ),
    	.rst_n                      (rst_n                    ),
    	.rx_data                    (rx_data                  ),
    	.rx_data_valid              (rx_data_valid            ),
    	.rx_data_ready              (rx_data_ready            ),
    	.rx_pin                     (uart_rx                  )
    );
    
    //实例化发送模块
    uart_tx#
    (
    	.CLK_FRE(CLK_FRE),
    	.BAUD_RATE(115200)
    ) uart_tx_inst
    (
    	.clk                        (clk                      ),
    	.rst_n                      (rst_n                    ),
    	.tx_data                    (tx_data                  ),
    	.tx_data_ready              (tx_data_ready            ),
    	.tx_data_valid              (tx_data_valid            ),
    	.tx_pin                     (uart_tx                  )
    );
    endmodule
    
    展开全文
  • STM32L4的串口打印程序欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左...

    简述

    基于HAL库改写的串口收发程序,基于Instance中断再双串口的情况下不能进入,改用串口中断函数,最后发现是没有在开始开串口中断,如下:

    HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer1, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
    HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer2, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
    

    但是此种情况下,智能接收定长数据,否则会出现接收不正确现象。

    串口接收定长回调函数

    下面是基于HAL库串口中断的 串口中断回调函数源码.

    // An highlighted block
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	while(HAL_UART_Transmit(&huart1, (uint8_t*)myBuffer, sizeof(myBuffer), 1000)!= HAL_OK); 
    	while(HAL_UART_Transmit(&huart1, (uint8_t*)aRxBuffer1, RXBUFFERSIZE, 1000)!= HAL_OK); 
    	while(HAL_UART_Transmit(&huart1, (uint8_t*)Enter, sizeof(Enter), 1000)!= HAL_OK);
    	buffer1[0] = *aRxBuffer1;
    	while(HAL_UART_Transmit(&huart2, (uint8_t*)myBuffer, sizeof(myBuffer), 1000)!= HAL_OK); 
    	while(HAL_UART_Transmit(&huart2, (uint8_t*)aRxBuffer2, RXBUFFERSIZE, 1000)!= HAL_OK); 
    	while(HAL_UART_Transmit(&huart2, (uint8_t*)Enter, sizeof(Enter), 1000)!= HAL_OK); 
    	buffer2[0] = *aRxBuffer2;
    	printf("%s\r\n", buffer1);
    	printf("%s\r\n", buffer2);
    }
    

    串口接收不定长数据回调函数

    串口接收不定长数据源码来源于网上大神,但是,此种方法虽然通用,但是由于采用延时计数器的方法来保存接收数据,这样,如果串口发送数据过快,接收数据不准确甚至后面的数据全都接收错误。

    void LPUART1_IRQHandler(void)// 低功耗串口1中断服务函数
    {
      if(__HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_RXNE))// 如果接收到一个字节
      {
        Res_Buf[Res_Count++]=hlpuart1.Instance->RDR; // 把数据保存到接收数组
        Res_Sign = 1; // 表示已经接收到数据
        Res_Times = 0; // 延时计数器清0
      }
    }
    

    程序解释

    程序里面有4个全局变量,分别是:

    unsigned char Res_Buf[256]; //接收数据的数组,用来接收串口数据
    unsigned char Res_Count=0; //接收数据的字节计数器,表示本次一帧数据包含几个字节
    unsigned char Res_Sign=0; //接收到数据标志,接收到1个字节就会置1
    unsigned char Res_Times=0; // 延时计数器,用来判断有没有接收完一帧数据
    

    在串口中断函数里面,每接收到一个字节,就会把接收到的字节保存到Res_Buf数组中,同时,字节计数器+1。然后把Res_Sign置1,表示已经接收到串口数据,但是,有没有接收完,是不一定的。在主函数当中,发现这个变量等于1了,就开始启动延时计数Res_Times,让这个变量++,只要延时到了5ms,就表示接收完一帧数据,退出do while后就可以开始处理数据了,但是,当接收到第二个字节以后,会在中断函数里面把Res_Times清0,也就是说,主函数里面的Res_Times++以后,白加了,只要有数据还没有接收完,这个Res_Times就会一直清0,如果串口接收能接收一万年也接收不完一帧数据,那一万年,Res_Times也到不了5。只有当再也没有串口数据过来了,Res_Times才会加到5,然后退出do while,表示接收完一帧数据了,可以开始处理了。

    总结

    如果接收定长数据的情况,可以用第一种方法,且可以适应高速传输,如果是在传输速度要求不高的情况下,可以采用第二种方法。

    展开全文
  • 用两张把FreeModbus在单片机串口上数据收发流程进行了梳理,脉络更加清晰。有了这样一个思路,可以更好地理解,移植FreeModbus的时候,为什么需要修改portserial.c和porttimer.c中的函数,和为什么需要修改串口...

    FreeModbus从站设计(9)-详解FreeModbus在单片机串口上的数据收发过程

    关键词:FreeModbus STM32F103 CubeMX HAL库 串口

    1.引言

    在上一篇文章中,主要阐述了vMBPortSerialEnable()这个函数如何基于HAL库调度单片机串口的收发,感觉还是不是很清晰,因此,孔丙火(微信公众号:孔丙火)在这一篇文章中,重点捋一下串口的收发函数调用关系,以求有有一个清晰的脉络。

    2.函数调用的基本框架

    直接上图,更清晰,接收过程如图1所示,发送过程如图2所示。

    图1

    接收过程起源于vMBPortSerialEnable()函数的调用,此时,该函数将串口设置位接收状态,即使能接收中断,禁止发送中断。从图中可以清晰的看出,需要修改的地方就是接收中断的回调函数和portserial.c和porttimer.c中的几个函数。至于何时调用vMBPortSerialEnable()函数,孔丙火(微信公众号:孔丙火)认为,我们是不需要关心的,只要按照之前的文章,把FreeModbus的代码添加到keil工程中,FreeModbus协议栈会进行调度。接收过程是一个字节一个字节进行接收的,当协议栈检测到定时器超时,则认为一个完整的数据帧接收完毕,开始进入数据处理的阶段,数据处理完成后,则进行回复数据的发送。

    图2

    发送过程同样起源于vMBPortSerialEnable()函数的调用,此时,该函数将串口设置位发送状态,即使能发送中断,禁止接收中断。从图中可以清晰的看出,需要修改的地方就是发送中断的回调函数和portserial.c中的几个函数。至于何时调用vMBPortSerialEnable()函数,孔丙火(微信公众号:孔丙火)认为,我们是不需要关心的,只要按照之前的文章,把FreeModbus的代码添加到keil工程中,FreeModbus协议栈会进行调度。发送过程同样是一个字节一个字节进行的,在xMBRTUTransmitFSM()函数中,会检测是否还有需要发送的数据,若没有数据需要发送了,则会调用vMBPortSerialEnable()函数,再次将串口设置为接收状态。作为Modbus从站,串口大部分时间是处于接收状态的。

    3.总结

    在这篇文章中,孔丙火(微信公众号:孔丙火)接着上一篇文章的思路,用两张图把FreeModbus在单片机串口上数据收发流程进行了梳理,脉络更加清晰。有了这样一个思路,可以更好地理解,移植FreeModbus的时候,为什么需要修改portserial.c和porttimer.c中的函数,和为什么需要修改串口中断的回调函数。从这篇文章中,也可以看出,采用HAL库是比较简单的,像是中断处理这些内容库函数都已经处理好了,很方便,可以提高开发效率。

    文章在公众号(孔丙火)同步推出,欢迎查看更多系列文章。

    单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。

    展开全文
  • 一、流程图本文以Android蓝牙客户端为例介绍从打开蓝牙到收发数据的完整流程,对蓝牙服务端程序不作介绍,请读者举一反三。下图为Android蓝牙客户端的流程图。下面分步详细介绍。文中只给出每一步的关键代码,整个...

    蓝牙串口模块是嵌入式设备上的常用模块,它可以方便地和手机建立连接,实时传输数据。本文介绍Android设备与蓝牙串口通信的具体实现。

    一、流程图

    本文以Android蓝牙客户端为例介绍从打开蓝牙到收发数据的完整流程,对蓝牙服务端程序不作介绍,请读者举一反三。下图为Android蓝牙客户端的流程图。

    下面分步详细介绍。文中只给出每一步的关键代码,整个例程的完整代码将在文末给出。

    二、打开蓝牙

    通过启动指定的Action来请求系统打开蓝牙。这几行代码的效果相当于点击手机顶部下拉框中的蓝牙按钮。

    // 获取蓝牙适配器

    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    //请求开启蓝牙

    if (!mBluetoothAdapter.isEnabled()) {

    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

    }

    三、搜索设备

    首先找出已匹配的蓝牙设备列表。

    // 将已配对的设备添加到列表中

    Set pairedDevices = mBluetoothAdapter.getBondedDevices();

    if (pairedDevices.size() > 0) {

    for (BluetoothDevice device : pairedDevices) {

    mDevicesArray.add(device.getName() + "\n" + device.getAddress());

    deviceList.add(device);

    }

    }

    再开始搜索附近的蓝牙设备,创建一个广播接收器以获取搜索结果。

    // 注册广播接收器,以获取蓝牙设备搜索结果

    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

    registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

    // 搜索蓝牙设备

    mBluetoothAdapter.startDiscovery();

    这里用到的广播接收器定义如下。注意最后需要调用notifyDataSetChanged方法,否则新搜索到的设备不会更新到ListView中。

    // Create a BroadcastReceiver for ACTION_FOUND

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

    public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();

    // When discovery finds a device

    if (BluetoothDevice.ACTION_FOUND.equals(action)) {

    // Get the BluetoothDevice object from the Intent

    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

    deviceList.add(device);

    // Add the name and address to an array adapter to show in a ListView

    mDevicesArray.add(device.getName() + "\n" + device.getAddress());

    // Notify ListView to update

    devicesListAdapter.notifyDataSetChanged();

    }

    }

    };

    四、匹配并建立连接

    通过调用BluetoothDevice对象的createRfcommSocketToServiceRecord方法可以获取一个socket。该方法需要传入蓝牙设备的UUID。通常,对于不同的服务需要设置不同的UUID,本文设置为针对蓝牙串口服务的UUID,其它常用的UUID可见文末的参考资料。成功获得socket对象之后,调用其上的connet方法即可匹配并建立连接。在Android系统中,不需要执行单独的匹配操作,建立连接时,系统会自动对未匹配的设备尝试匹配,必要时会要求用户输入Pin码。

    BluetoothSocket socket = null;

    try {

    // 蓝牙串口服务对应的UUID。如使用的是其它蓝牙服务,需更改下面的字符串

    UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    socket = device.createRfcommSocketToServiceRecord(MY_UUID);

    } catch (Exception e) {

    Log.d("log", "获取Socket失败");

    return;

    }

    try {

    // Connect the device through the socket. This will block

    // until it succeeds or throws an exception

    socket.connect();

    Log.d("log", "连接成功");

    } catch (IOException connectException) {

    // Unable to connect; close the socket and get out

    Log.d("log", "连接失败");

    try {

    socket.close();

    } catch (IOException closeException) { }

    return;

    }

    五、收发数据

    只需要从socket中获取输入输出流,再调用read和write方法就可以实现数据的发送与接收。原理很简单,不过具体使用时需要考虑如何循环读取数据。一般是创建一个新线程专门用于读取数据,否则read方法将会阻塞主线程。下面的代码只说明了数据收发的基本用法。

    mmInStream = socket.getInputStream();

    mmOutStream = socket.getOutputStream();

    //接收数据

    byte[] buffer = new byte[1024];

    try {

    bytes = mmInStream.read(buffer);

    } catch (IOException e) {

    break;

    }

    //发送数据

    try {

    mmOutStream.write(bytes);

    } catch (IOException e) { }

    六、总结

    本文把Android与蓝牙串口通信的整个流程做了最大程度上的简化,只突出核心功能代码。而具体应用时则需要考虑更多的实际情况,包括界面的布局等等。关于Android系统针对蓝牙设备提供的API的总体介绍,建议阅读参考资料中的第一篇。

    下图是在华为手机上的运行效果。

    QQ图片20170611204323.png

    七、参考资料

    展开全文
  • 实验结果五、实验二——串口收发数据1. 实验目的2. 再多学一个寄存器和标志位:IEN0、URX0IF3. 程序流程4. 完整代码六、实验三——串口控制LED1. 实验目的2. 实验代码 一、补充基础知识 在CC2530 中,U
  • 设计一个串口程序,该程序执行时,具有通信参数选择及串口收发数据功能,界面友好。 2 设计流程图及说明 如图1.1所示,程序运行后,首先进入功能选择,共4中功能可供选择,分别为接收数据,发送数据,接收文件,...
  • 在工作中经常会用到USB转TTL串口调试工具。网上买的用的不顺手,所以做了一个。最终效果:渲染实物学习目的做一个USB转...验证功能是否正常5V电压:3.3V电压串口收发 如何使用keyshot制作渲染:大概流程 先立创...
  • STM8L的USART1串口通信详解 含例程

    千次阅读 2019-03-09 16:22:12
    STM8L除了可以进行串口通信,还可支持红外通信,智能卡协议,这些功能后续...下串口发送数据流程。对于串口发送数据,需要注意的是,打开发送后,数据发送完成,如果不关闭中断,程序会一直进入中断。所以在确保...
  • 在分析CC2430性能的基础上,设计一种基于DMA的节点收发机制,配置DMA的源和目的地址寄存器、数据长度寄存器以及触发模式寄存器,并根据DMA操作流程给出数据发送和接收流程图。基于TinyOS的组件结构,给出了包括DMA...
  • Linux C编程连载(3)-串口编程

    千次阅读 2011-09-23 20:23:33
    设计一个串口程序,该程序执行时,具有通信参数选择及串口收发数据功能,界面友好。 【设计流程图】  如图1.1所示,程序运行后,首先进入功能选择,共4中功能可供选择,分别为接收数据,发送数据,接收文件,发送...
  • Ntrip协议 在RTK接收机中,有多种方式获取到差分数据:电台、挂外串口、网络等。其中网络方式适合在有网络基准站的地区,省掉携带多一台RTK接收机来完成工作。 CORS是网络基准站,通过...下面看整个访问的流程图。 ...
  • GSMinitAT指令

    2009-08-29 16:46:15
    GSM初始化AT指令及解释和短信收发系统主程序流程图串口中断流程图.
  • #STM32库函数学习笔记

    2020-09-06 11:00:09
    ## #USART串口STMF103RB库函数编程笔记之串口收发USARTuart.c代码stm32f10xit.c中新建一个中断处理函数 帮助文档 快捷键目录标题文本样式列表链接代码片表格注脚注释自定义列表LaTeX 数学公式插入甘特插入UML...
  • 特权同学图书《Altera FPGA伴你玩转USB3.0与LVDS》扫描版。 编辑推荐 (1)《Altera FPGA伴你玩转USB...第7章讲解如何使用FPGA实现LVDS接口应用,包括LVDS液晶屏的驱动、LVDS收发设计以及包含CRC校验的LVDS收发设计。
  • 6.11 UART串口收发测试 6.11.1 功能概述 6.11.2 设计说明 6.11.3 源码解析 6.11.4 板级调试 第7章 片内资源应用 7.1 PLL配置 7.1.1 功能概述 7.1.2 源码解析 7.1.3 板级调试 7.2 片内存储器应用之ROM 7.2.1 功能...
  • 3.5串口芯片驱动安装 3.5.1驱动安装 3.5.2设备识别 3.6USB 3.0控制器FX3的SDK安装 3.7USB 3.0控制器FX3的驱动安装 3.7.1PC与开发板的USB 3.0连接 3.7.2PC与USB连接 3.7.3USB 3.0控制器FX3驱动安装 Xilinx ...
  • 北航3系大四要调小车在赛道上跑,小车单片机用的K60,老师提供的代码里还有串口收发的库,就想着用蓝牙模块再开发上位机调试软件进行远程调试,正好借此机会学习了一番PyQt。现在从头总结一下开发流程~想从0开发的...
  • 不定长数据接收ADC模块基于python的波形显示python串口读取插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也...
  • 一块LPC824breakout开发板,一个HS0038的红外接收管,串口收发。 红外解码分析仪电路原理如下,HS0038接PIO0_18管脚,LPC824通过测量PIO0_18管脚的高低电平时间来采集红外数据。 软件流程介绍: 单片机上电,打开...
  • 主节点程序流程图: 终端节点程序流程图: 上位机网络管理软件设计: 无线传感网络的上位机网络管理软件是在VC6.0 开发环境下开发完成。界面编程利用MFC 向导生成。串口编程利用了微软Visual Basic中提供的一个串口控件...
  • 送流程如下,先接收I2C控制指令,寄存器2接收控制指令,选择超声波探头号(因为主控板接 12 个收发一体式防水探头,每个探头独立工作,每个探头占据8位地址,分别对应8种不同的数据收发方式),流程图如下所示: ...
  • 软件介绍: CH9121配置工具内附使用说明和电路原理。运行后选择所使用的网络适配器,在设备列表中...4. 此时计算机端串口调试软件显示 CH9121 模块客户端已连接,选好串口参数,打开串口即可进行数据收发测试
  • 子控件包括饼图+圆环+曲线+柱状+柱状分组+横向柱状+横向柱状分组+合格率控件+百分比控件+进度控件+设备状态面板+表格数据+地图控件+视频控件等。 二级界面可以自由拖动悬浮,支持最小化最大化关闭,响应...
  • ZigBee2007PRO协议栈实验与实践.pdf

    热门讨论 2014-12-24 23:49:08
     8.7.2 灯和控制器主函数程序流程图240  8.7.3 其他初始化关键函数242  8.7.4 网络状态变化函数243  8.7.5 绑定相关函数246  8.8 操作系统250  8.8.1 操作系统关键参数251  8.8.2 操作系统关键函数252  8.9 ...
  • 全球首款100%开源(智能手环/表)穿戴开发套件 一、资料下载地址 ...id=522905240089 二、联系方式 QQ技术交流群:金刚狼-(hardware)群 193836402 ...三、资料目录 1.DA14580相关英文文档资料 ...四、学习流程图
  • Windows CE工程实践完全解析(pdf)

    热门讨论 2011-11-12 18:59:56
    5.1 览windows ce的初始化启动流程 5.2 开天辟地的startup函数 5.3 kemelstart函数 5.4 armlnit函数 5.5 kernel.d11模块的入口点函数nkstartup 5.6 armsetup函数 5.7 kemelstart函数  第6章 案例解说windows ce ...
  • 5.1 览windows ce的初始化启动流程 5.2 开天辟地的startup函数 5.3 kemelstart函数 5.4 armlnit函数 5.5 kernel.d11模块的入口点函数nkstartup 5.6 armsetup函数 5.7 kemelstart函数  第6章 案例解说windows ce ...
  • 3.软件设计:程序流程图,编程,修改,下载到芯片对电路进行调试并分析。 4.毕业论文,修改,打印论文 5.毕业设计答辩 预期效果: 1.能使单片机红外发射和接收硬件模块,温度测量模块,LED显示模块的信息。 2.能使...

空空如也

空空如也

1 2 3
收藏数 60
精华内容 24
关键字:

串口收发流程图