整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          STM32 學(xué)習(xí)8 USART串口通訊與printf

          STM32 學(xué)習(xí)8 USART串口通訊與printf重定向


          、串口通信介紹

          STM32 F103ZET6包含多個UART、USART串口。

          1. USART介紹

          USART,全稱:Universal Synchronous/Asynchronous Receiver/Transmitter,是通用同步/異步串行接收/發(fā)送器,主要特點有:

          • 同步和異步通信
          • 全雙工通信
          • 支持硬件和軟件流控制機制

          2. UART介紹

          UART,全稱:Universal Asynchronous Receiver/Transmitter,是通用異步收發(fā)器,在USART功能的基礎(chǔ)上,裁剪掉了同步通信功能,其主要特點:

          • 異步通信
          • 全雙工通信
          • 無需外部時鐘信號

          3. STM32 F103ZET6串口資源

          STM32 F103ZET6芯片,有5個USART接口,數(shù)據(jù)手冊可在官網(wǎng)查詢:
          https://www.st.com/zh/microcontrollers-microprocessors/stm32f103.html

          根據(jù)手冊的描述:

          STM32F103xC、STM32F103xD和STM32F103xE性能型系列集成了:

          • 三個通用同步/異步串行收發(fā)器(USART1、USART2和USART3)
          • 兩個通用異步串行收發(fā)器(UART4和UART5)。

          這五個接口提供了異步通信、IrDA SIR ENDEC支持、多處理器通信模式、單線半雙工通信模式,并具有LIN主/從能力。USART1接口能夠以高達4.5 Mbit/s的速度進行通信。其他可用的接口的通信速度為最高2.25 Mbit/s。

          USART1、USART2和USART3還提供CTS和RTS信號的硬件管理、智能卡模式(符合ISO 7816標(biāo)準(zhǔn))以及類SPI通信功能。
          除了UART5外,所有接口都可以由DMA控制器服務(wù)。

          開發(fā)板原理圖:



          4. STM32 USART作用

          USART 一個常見應(yīng)用是將printf 函數(shù)通過串口輸出,方便程序調(diào)試。
          另外, USART還支持 LIN(域互連網(wǎng)絡(luò))、智能卡協(xié)議與紅外IrDA協(xié)議 SIR ENDEC規(guī)范、調(diào)制解調(diào)器操作(CTS/RTS)、和DMA功能。

          5. STM32 USART框圖

          在《stm3210x參考手冊.pdf》P309可以看到STM32的USART框圖:



          引腳說明

          • TX:發(fā)送端口;
          • RX:接收端口;
          • nRTS、nCTS:硬件流控,不常使用,只針對異步串口通訊端口;
          • SCLK:時鐘,只針對異步串口通訊端口;
          • IRDA_OUT、IRDA_IN:內(nèi)部引腳。

          6. 寄存器

          這里簡單列出常用的USART寄存器,詳細使用方法可以參考《stm32中文參考手冊.pdf》。

          USART_SR(Status Register,狀態(tài)寄存器):

          用于存儲USART的狀態(tài)信息,包括發(fā)送完成、接收緩沖區(qū)非空、校驗錯誤等。

          USART_DR(Data Register,數(shù)據(jù)寄存器):

          用于存儲發(fā)送和接收的數(shù)據(jù)。寫入此寄存器可以啟動數(shù)據(jù)發(fā)送,讀取此寄存器可以獲取接收到的數(shù)據(jù)。

          USART_BRR(Baud Rate Register,波特率寄存器):

          用于設(shè)置USART的波特率,通常需要根據(jù)系統(tǒng)時鐘和所需的波特率進行配置。

          USART_CR1(Control Register 1,控制寄存器1):

          用于配置USART的工作模式、數(shù)據(jù)格式、中斷使能等。

          USART_CR2(Control Register 2,控制寄存器2):

          用于配置USART的硬件流控、時鐘極性等特性。

          USART_CR3(Control Register 3,控制寄存器3):

          用于配置USART的其他特性,如DMA使能、多主機模式等。

          USART_GTPR(Guard Time and Prescaler Register,守護時間和預(yù)分頻器寄存器):

          用于配置USART的守護時間和時鐘預(yù)分頻器,通常與同步通信相關(guān)。

          USART_IT(Interrupt Register,中斷寄存器):

          用于配置USART的中斷使能和中斷標(biāo)志位。

          7. 中斷請求

          在《STM32中文參考手冊》中,中斷請求表:



          二、開發(fā)板RS-232硬件連接

          在普中-F1開發(fā)板上,提供了 RS-232 母頭,其線序:



          可以使用一根 RS-232轉(zhuǎn)TTL轉(zhuǎn)USB的連接線,連接USB接電腦,電腦上使用串口調(diào)試工具進行開發(fā)實驗。

          RS-232公頭線序:.



          三、串口通信的配置步驟

          配置USART的步驟如下:

          1.時鐘使能:

          首先需要使能USART所使用的時鐘,確保其正常工作。USART掛接的系統(tǒng)總線:

          • USART1: APB2時鐘總線
          • USART2~5:APB1時鐘總線

          代碼示例:

          // 使能 GPIOA 時鐘
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
          // 使能 USART1 時鐘
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

          2.GPIO配置:

          配置相關(guān)的GPIO引腳,將其設(shè)置為USART的TX(發(fā)送)和RX(接收)功能。此外,還需要配置引腳的模式(輸入/輸出)、速率、上拉/下拉等參數(shù)。
          代碼示例:

          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
          // 接收設(shè)置輸入浮空模式
          GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;

          3.USART參數(shù)配置:

          配置USART的工作模式、波特率、數(shù)據(jù)位、停止位、校驗位等參數(shù)。這些參數(shù)需要根據(jù)具體的通信需求進行設(shè)置。

          #include "stm32f10x.h"
          
          void USART1_UART_Init(void)
          {
              GPIO_InitTypeDef GPIO_InitStruct;
              USART_InitTypeDef USART_InitStruct;
          
              // 使能串口1時鐘
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
          
              // 串口1 GPIO初始化
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA時鐘
              GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; // TX引腳
              GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; // 復(fù)用推挽輸出模式
              GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; // GPIO速度為50MHz
              GPIO_Init(GPIOA, &GPIO_InitStruct);
          
              GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; // RX引腳
              GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 浮空輸入模式
              GPIO_Init(GPIOA, &GPIO_InitStruct);
          
              // 串口1參數(shù)配置
              USART_InitStruct.USART_BaudRate=115200; // 波特率為115200
              USART_InitStruct.USART_WordLength=USART_WordLength_8b; // 數(shù)據(jù)位長度為8位
              USART_InitStruct.USART_StopBits=USART_StopBits_1; // 停止位為1位
              USART_InitStruct.USART_Parity=USART_Parity_No; // 無校驗位
              USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 無硬件流控制
              USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; // 支持接收和發(fā)送
              USART_Init(USART1, &USART_InitStruct);
          
              // 使能USART1模塊
              USART_Cmd(USART1, ENABLE);
          }
          

          4.使能USART:

          最后需要使能USART模塊,開始其正常工作。這涉及到設(shè)置相應(yīng)的控制寄存器來啟動USART的發(fā)送和接收功能。

          void USART_Cmd(USAR_TypeDef* USARTx, FunctionalState NewState);
          USART_Cmd(USART1, ENABLE);

          5. 設(shè)置串口中斷類型并使能

          本章使用使用到串口中斷,關(guān)于中斷具體概念和使用會在后續(xù)章節(jié)介紹。

          // 設(shè)置中斷類型
          void USART_ITConfig(USART_TypeDef * USARTx,uint16_t USART_IT,FunctionalState NewState);
          // 串口1接收使能
          USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);
          // 發(fā)送使能
          USART_ITConfig(USART1, USART_IT_TC,ENABLE);

          6. 設(shè)置串口中斷優(yōu)先級、使能串口中斷通道

          NVIC_Init()

          7. 串口中斷函數(shù)

          ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
          if(USART_GetITStatus(USART1, USART_IT_RXNE) !=RESET){
            // 接收USART1 中斷的處理
          }
          void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

          四、代碼實現(xiàn)

          下面代碼實現(xiàn)的功能是:
          通過電腦串口給開發(fā)板的USART3發(fā)送數(shù)據(jù)0-9的數(shù)據(jù),開發(fā)板的數(shù)碼管顯示對應(yīng)的數(shù)值,并且回復(fù)同樣的內(nèi)容給開發(fā)板。

          1. 用 uart 庫



          2. usart_utils.c

          #include "usart_utils.h"
          
          #include "stm32f10x.h"
          #include "led_utils.h"
          
          // 初始化USART3
          void USART3_Init(u32 bound) {
              GPIO_InitTypeDef GPIO_InitStructure;
              USART_InitTypeDef USART_InitStructure;
              NVIC_InitTypeDef NVIC_InitStructure;
          
              // 使能USART3和GPIOB時鐘
              RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
          
              // 配置USART3的GPIO引腳
              GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; // TX引腳
              GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
              GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
              GPIO_Init(GPIOB, &GPIO_InitStructure);
          
              GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11; // RX引腳
              GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
              GPIO_Init(GPIOB, &GPIO_InitStructure);
          
              // 配置USART3參數(shù)
              USART_InitStructure.USART_BaudRate=9600;
              USART_InitStructure.USART_WordLength=USART_WordLength_8b;
              USART_InitStructure.USART_StopBits=USART_StopBits_1;
              USART_InitStructure.USART_Parity=USART_Parity_No;
              USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
              USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
              USART_Init(USART3, &USART_InitStructure);
          
              // 使能USART3
              USART_Cmd(USART3, ENABLE);
          
              USART_ClearFlag(USART3, USART_FLAG_TC);
              USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
          
              NVIC_InitStructure.NVIC_IRQChannel=USART3_IRQn;
              NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
              NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
              NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
              NVIC_Init(&NVIC_InitStructure);
              
          }
          
          
          // USART3中斷服務(wù)函數(shù)
          void USART3_IRQHandler(void) {
              if (USART_GetITStatus(USART3, USART_IT_RXNE) !=RESET) {
                  // 讀取接收到的數(shù)據(jù)
                  u8 data=USART_ReceiveData(USART3);
                  if(data<=9){
                      // 收到什么,就發(fā)送什么
                      USART3_SendData(data);
                      // led也顯示對應(yīng)的值
                      led_lightn(data);
                  }
          
                  // 清除接收中斷標(biāo)志位
                  USART_ClearITPendingBit(USART3, USART_IT_RXNE);
              }
          }
          
          // 發(fā)送函數(shù)
          void USART3_SendData(u8 data) {
              USART_SendData(USART3, data);
              while (USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET);
          }
          

          3. usart_utils.h

          #ifndef __USART_UTILS_H__
          #define __USART_UTILS_H__
          #include "stm32f10x.h"
          
          void USART3_Init(u32 bound);
          void USART3_IRQHandler(void);
          // 發(fā)送函數(shù)
          void USART3_SendData(u8 data);
          #endif
          

          4. main函數(shù)

          #include "gpio_utils.h"
          #include "rcc_utils.h"
          #include "stm32f10x.h"
          #include "sys_tick_utils.h"
          #include "led_utils.h"
          #include "key_utils.h"
          #include "usart_utils.h"
          
          // 主函數(shù)
          int main(void)
          {
          	GPIO_Configuration(); // 調(diào)用GPIO配置函數(shù)
          	sys_tick_init(72);
          	led_all_off();
          	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
          	USART3_Init(9600);
          
          	while (1) // 無限循環(huán)
          	{
          		delay_ms(20);
          	}
          }
          

          五、printf重定向

          1. 實現(xiàn)方式

          通過printf的重定向 ,可以實現(xiàn)在打印printf內(nèi)容時,通過串口將內(nèi)容輸出來,以方便調(diào)試。

          要在 STM32 上實現(xiàn) printf 的重定向,通常需要重寫 fputc 函數(shù),以便將輸出重定向到你所選擇的串口。下面是一個基本的示例:

          #include <stdio.h>
          #include "usart_utils.h"
          
          // 重定向 fputc 函數(shù),將輸出重定向到 USART3
          int fputc(int ch, FILE *f) {
              USART_SendData(USART3, (uint8_t)ch);
              while (USART_GetFlagStatus(USART3, USART_FLAG_TXE)==RESET);
              return ch;
          }

          在這個示例中, fputc 函數(shù)會重定向到 USART3,并且通過調(diào)用 USART_SendData 函數(shù)發(fā)送一個字節(jié)到 USART3,然后等待發(fā)送完成。

          需要注意的是,使用這個功能時,要鉤選microLIB功能。 鉤選microLIB會增加程序的體積,需要權(quán)衡利弊。

          2. 調(diào)用

          示例代碼里,使用printf("HelloWorld");,就可以在串口看到輸出的字符串。

          本文代碼開源在:
          https://gitee.com/xundh/stm32_arm_learn

          一小節(jié)分享了在串口中斷中進行實時協(xié)議解析的一種處理方法,僅適用于一些需要對串口指令實時處理的特殊場合。大部分情況下,串口中斷服務(wù)函數(shù)中是不建議太多的處理代碼的,一般可以在串口中斷中完成數(shù)據(jù)的接收,通過串口中斷將數(shù)據(jù)接收到數(shù)據(jù)緩沖區(qū)中【最好的方式是采用環(huán)形隊列】,本節(jié)代碼即是采用超時接收的處理方式,在串口中斷中進行數(shù)據(jù)的接收,在定時中斷中進行斷幀,實現(xiàn)數(shù)據(jù)的接收【前面小節(jié)介紹過此方法】,然后在主程序中對數(shù)據(jù)協(xié)議進行解析處理。本小節(jié)代碼和前面不同的地方在于舍棄了幀頭幀尾的判斷,增加了CRC校驗的功能,自定義的協(xié)議格式,參考的是MODBUS通信協(xié)議進行設(shè)計的。因此本小節(jié)的代碼,也可以用于RS485通信中,只是不是標(biāo)準(zhǔn)的MODBUS協(xié)議罷了,是自定義的一種協(xié)議。關(guān)于自定義協(xié)議的分析,大家可以查找網(wǎng)絡(luò)上的相關(guān)文檔進行了解,也可以參看本人的視頻課程:串口中斷即時解析用戶自定義通訊協(xié)議(接收數(shù)據(jù)字節(jié)固定)的編程,視頻地址鏈接:https://www.ixigua.com/6846389376847610376中的具體介紹。另外,如果協(xié)議指令只是用于控制如一個單片機控制板中的不同的器件時,如多路繼電器等,也可以省略地址信息,如果用于多從機的控制,可以加上地址,大家可以根據(jù)具體的應(yīng)用場合舉一反三,適當(dāng)?shù)恼{(diào)整協(xié)議指令格式,靈活處理。本小節(jié)代碼對應(yīng)的單片機視頻課程為:串口超時接收用戶自定義通訊協(xié)議(基于CRC16校驗)的編程實現(xiàn),視頻課程地址鏈接為:https://www.ixigua.com/6847871377668571652。話不多說,下面是具體的代碼,大家可以適當(dāng)?shù)膮⒖枷拢胁划?dāng)之處的歡迎評論區(qū)留言討論。

          /*
          ******************************************
          *
          * 文件名:main.c
          * 描  述:主程序
          * 版本號:
          * 備  注:
          ******************************************
          */
          /************************************************************************************
          功能:帶CRC校驗的 自定義通訊協(xié)議
                01      01       02     03     CRC16/MODBUS
          		 地址   功能字         數(shù)據(jù)         校驗字
               
          		例如:      
          			01  01  80  80  30  78  LED點亮      0x8080毫秒		
          		  01  02  80  80  C0  78  蜂鳴器鳴響   0x8080毫秒	
              CRC16.c中的CRC校驗算法計算得到的CRC校驗字和在線工具中
          		計算的CRC校驗字高低字節(jié)正好相反。
              CRC在線計算工具網(wǎng)址:http://www.ip33.com/crc.html
          		可以根據(jù)自己的應(yīng)用需求進行協(xié)議的擴展,如控制多路繼電器,
          		同時還可以擴展到RS485多從機系統(tǒng)中使用
          **************************************************************************************/
          //頭文件
          #include <reg51.h>
          #include "delay.h"
          #include "uart.h"
          #include "CRC16.h"
          #include "time.h"
          
          //本機地址
          #define LOCAL_ADRRESS 0x01
          
          void main(void)
          {
          	unsigned char i;
          	unsigned int crc;
          	unsigned char crch,crcl;
          	Timer0Init();//定時器初始化
          	UartInit();//串口初始化
          	EA=1;//開總中斷
          	DelayXms(10);
          	while(1)
          	{
          		if(recv_flag)//接收到數(shù)據(jù)
          		{
          			recv_flag=0;//清零接收標(biāo)志位
          			start_timer=0;//關(guān)軟件定時器
          			//校驗本機地址,是本機的,則接收處理,否則退出
          			if(recv_buf[0] !=LOCAL_ADRRESS)			
          			{
          				return;
          			}
          			//CRC16校驗,校驗正確,我們才處理,否則直接返回,并給出錯誤碼
          			crc=GetCRC16(recv_buf,recv_cnt - 2);
          			crch=crc >> 8;
          			crcl=crc & 0xFF;
          			if((recv_buf[recv_cnt - 2] !=crch) || (recv_buf[recv_cnt - 1] !=crcl))
          			{
          				recv_buf[1]=recv_buf[1] | 0x80;
          				crc=GetCRC16(recv_buf,recv_cnt - 2);
          				recv_buf[4]=crc & 0xFF;
          				recv_buf[5]=crc >> 8;
          				for(i=0;i<recv_cnt;i++)
          				{
          					sendByte(recv_buf[i]);
          				}
          				return;
          			}
          			switch(recv_buf[1])//命令字/功能字判斷
          			{
          				case 1:led_data=recv_buf[2];
          							 led_data=led_data << 8;
          							 led_data=led_data + recv_buf[3];//取數(shù)據(jù)
          							 led_cnt=0;//目的是使LED點亮上述接收的數(shù)據(jù)的時間
          					break;
          				case 2:beep_data=recv_buf[2];
          							 beep_data=beep_data << 8;
          							 beep_data=beep_data + recv_buf[3];//取數(shù)據(jù)
          							 beep_cnt=beep_data;//蜂鳴器鳴響時間數(shù)據(jù)
          					break;
          				default:break;
          			}
          			//返回接收數(shù)據(jù) 測試用
          			for(i=0;i<recv_cnt;i++)
          			{
          				sendByte(recv_buf[i]);
          			}
          			clr_recvbuffer(recv_buf);//清除緩沖buffer
          			recv_cnt=0;
          		}
          	}
          }
          /*
          ******************************************
          *
          * 文件名:time.c
          * 描  述:定時/計數(shù)器0初始化及中斷處理
          * 版本號:
          * 備  注:
          ******************************************
          */
          #include "time.h"
          #include "uart.h"
          
          //全局變量定義
          unsigned int led_data;
          unsigned int beep_data;
          unsigned int led_cnt;
          unsigned int beep_cnt;
          
          void Timer0Init(void)		//1毫秒@11.0592MHz
          {
          	TMOD &=0xF0;		//設(shè)置定時器模式
          	TMOD |=0x01;		//設(shè)置定時器模式
          	TL0=0x66;		//設(shè)置定時初值
          	TH0=0xFC;		//設(shè)置定時初值
          	TF0=0;		//清除TF0標(biāo)志
          	ET0=1;
          	TR0=1;		//定時器0開始計時
          }
          
          void timer0_ISR() interrupt 1
          {
          	TR0=0;
          	if(start_timer==1)
          	{
          		recv_timer_cnt++;//1、累加定時時間計數(shù)器
          		if(recv_timer_cnt > MAX_REV_TIME)//2、判斷定時時間是否超過了設(shè)定的最大的閾值,
          			                               //超過則說明等待一段時間后沒有新的數(shù)據(jù)到,我們
          		                                   //判斷一包數(shù)據(jù)接收完畢
          		{
          			recv_timer_cnt=0;//3、清除定時計數(shù)器 處理數(shù)據(jù) 清除buffer(放到數(shù)據(jù)處理之后)
          			recv_flag=1;		
          		}
          	}	
          	//LED點亮?xí)r間控制
          	if(led_cnt < led_data)
          	{
          		led_cnt++;
          		LED=0;
          	}
          	else
          	{
          		LED=1;
          	}
          	//蜂鳴器鳴響時間控制
          	if(beep_cnt !=0)
          	{
          		beep_cnt--;
          		BEEP=~BEEP;
          	}
          	TL0=0x66;		//設(shè)置定時初值
          	TH0=0xFC;		//設(shè)置定時初值
          	TR0=1;
          }
          //time.h
          #ifndef __TIME_H__
          #define __TIME_H__
          
          #include <reg51.h>
          
          sbit LED=P1^0;
          sbit BEEP=P3^7;
          
          extern unsigned int led_data;
          extern unsigned int beep_data;
          extern unsigned int led_cnt;
          extern unsigned int beep_cnt;
          
          void Timer0Init(void);
          
          #endif
          /*
          **************************************
          *
          * 文件名:uart.c
          * 描  述:串口處理
          * 版本號:
          * 備  注:
          **************************************
          */
          
          #include "uart.h"
          
          unsigned char recv_flag=0;
          unsigned char start_timer=0;
          unsigned char recv_buf[MAX_REV_NUM];
          unsigned char recv_cnt;
          unsigned char recv_timer_cnt;
          
          void UartInit(void)		//9600bps@11.0592MHz
          {
          	PCON &=0x7F;		//波特率不倍速
          	SCON=0x50;		//8位數(shù)據(jù),可變波特率
          	TMOD &=0x0F;		//清除定時器1模式位
          	TMOD |=0x20;		//設(shè)定定時器1為8位自動重裝方式
          	TL1=0xFD;		//設(shè)定定時初值
          	TH1=0xFD;		//設(shè)定定時器重裝值
          	ET1=0;		//禁止定時器1中斷
          	ES=1;
          	TR1=1;		//啟動定時器1
          }
          
          void sendByte(unsigned char dat)
          {
          	SBUF=dat;
          	while(!TI);
          		TI=0;
          }
          
          void sendString(unsigned char *dat)//Hello World!
          {
          	while(*dat !='\0')
          	{
          		sendByte(*dat++);
          	}
          }
          
          char putchar(char c)
          {
          	sendByte(c);
          	return c;
          }
          void clr_recvbuffer(unsigned char *buf)
          {
          	unsigned char i;
          	for(i=0;i<MAX_REV_NUM;i++)
          	{
          		buf[i]=0;
          	}
          }
          /*************************************
          1、中斷服務(wù)函數(shù)一定是一個沒有返回值的函數(shù)
          2、中斷服務(wù)函數(shù)一定是沒有參數(shù)的函數(shù)
          3、中斷服務(wù)函數(shù)函數(shù)名后跟關(guān)鍵字 interrupt 
          4、interrupt n 0 - 4   5個中斷源     8*n + 0003H  
              0003H INT0 000BH T0  0013H INT1 001BH T1 0023H ES
          5、中斷服務(wù)函數(shù)不能北主程序或其他程序所調(diào)用
          6、 n 后面 跟 using m (0 - 3 )工作寄存器組
          ***************************************/
          void uart_ISR() interrupt 4
          {
          	if(RI)
          	{
          		RI=0;
          		start_timer=1;//1、每接收一幀數(shù)據(jù)的時候,打開軟件定時器,去計數(shù)
          		if(recv_cnt < MAX_REV_NUM)
          		{	
          			recv_buf[recv_cnt]=SBUF;//2、接收數(shù)據(jù)到數(shù)據(jù)緩沖區(qū),注意緩沖區(qū)的大小范圍問題
          			recv_cnt++;
          		}
          		else
          		{
          			recv_cnt=MAX_REV_NUM;
          		}
          		recv_timer_cnt=0;//3、每接收一幀數(shù)據(jù),記得把定時計數(shù)清零 相當(dāng)于是喂狗信號
          		                   //但是在定時中斷里面會不斷累加
          	}
          	if(TI)
          	{
          		TI=0;
          	}
          }
          
          //uart.h
          #ifndef __UART_H__
          #define __UART_H__
          
          #include <reg51.h>
          #include <stdio.h>
          
          #define MAX_REV_NUM   20
          #define MAX_REV_TIME  5
          
          extern unsigned char start_timer;
          extern unsigned char recv_buf[MAX_REV_NUM];
          extern unsigned char recv_cnt;
          extern unsigned char recv_timer_cnt;
          extern unsigned char recv_flag;
          
          void UartInit(void);
          void sendByte(unsigned char dat);
          void sendString(unsigned char *dat);
          void clr_recvbuffer(unsigned char *buf);
          
          #endif
          /*
          *********************
          *
          * 文件名:CRC16.c
          * 描  述:通用的CRC16校驗算法文件
          * 版本號:v1.0
          * 備  注:此文件代碼源自網(wǎng)絡(luò)
          *******************************************************************************
          */
          #include "CRC16.h"
          /* CRC16計算函數(shù),ptr-數(shù)據(jù)指針,len-數(shù)據(jù)長度,返回值-計算出的CRC16數(shù)值 */
          unsigned int GetCRC16(unsigned char *ptr,  unsigned char len)
          { 
              unsigned int index;
              unsigned char crch=0xFF;  //高CRC字節(jié)
              unsigned char crcl=0xFF;  //低CRC字節(jié)
              unsigned char code TabH[]={  //CRC高位字節(jié)值表
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
                  0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
                  0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
                  0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
                  0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
                  0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
                  0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
                  0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
                  0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
                  0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
                  0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
                  0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
                  0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
                  0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
                  0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  
              } ;  
              unsigned char code TabL[]={  //CRC低位字節(jié)值表
                  0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  
                  0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  
                  0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
                  0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  
                  0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  
                  0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
                  0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  
                  0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  
                  0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
                  0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  
                  0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  
                  0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
                  0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  
                  0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  
                  0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
                  0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  
                  0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  
                  0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
                  0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  
                  0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  
                  0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
                  0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  
                  0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  
                  0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
                  0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  
                  0x43, 0x83, 0x41, 0x81, 0x80, 0x40  
              } ;
           
              while (len--)  //計算指定長度的CRC
              {
                  index=crch ^ *ptr++;
                  crch=crcl ^ TabH[index];
                  crcl=TabL[index];
              }
              
              return ((crch<<8) | crcl);  
          }
          //CRC16.h
          #ifndef __CRC16_H__
          #define __CRC16_H__
          
          unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);
          
          #endif

          delay.c 和delay.h前期文章中已經(jīng)分享過,本節(jié)不再單獨貼出。通過Proteus仿真和虛擬串口,可以對上述的代碼功能進行簡單的測試,當(dāng)然也可以通過開發(fā)板或自制通信板進行相關(guān)的測試,具體的測試結(jié)果如下所示:

          LED燈點亮命令測試

          蜂鳴器鳴響命令測試

          CRC校驗錯誤測試

          串口通信的相關(guān)代碼分享告一段落,其他串口相關(guān)的介紹,可以關(guān)注后,查看主頁單片機視頻集合中的單片機應(yīng)用實踐篇中的相關(guān)視頻內(nèi)容,其他的幾個視頻的代碼就不再單獨貼出了,因為根據(jù)最近幾篇文章分享的模塊化代碼,很容易的就可以根據(jù)視頻的功能要求,調(diào)試出來相應(yīng)的程序來,沒有必要每個視頻都貼出源碼的。另外視頻代碼僅供大家參考,Bug在所難免,主要是示例程序代碼,很多的功能并不完善,希望大家可以根據(jù)這種編程思路,進行相應(yīng)的拓展。主要是感覺串口通信自定義協(xié)議是一個重點,也是一個難點,很多初學(xué)的同學(xué),對此會感覺比較難,實際上,如果理解透徹了,也就不覺得難了的。同時,串口如果掌握好了,后面很多的器件或模塊接口都是采用串口通信,學(xué)習(xí)起來也就輕松了許多,比如藍牙模塊,WIFI模塊,4G模組,RFID等。所以,有必要把串口好好的學(xué)一下,歡迎大家關(guān)注,留言討論。

          片機與Labview串口通信

          單片機與LabVIEW之間的串口通信是一種常見的數(shù)據(jù)傳輸方式,它允許單片機與計算機之間進行實時數(shù)據(jù)交換和控制。LabVIEW作為一種功能強大的圖形化編程軟件,具有友好的用戶界面和強大的數(shù)據(jù)處理能力,被廣泛應(yīng)用于工程實踐和科學(xué)研究中。通過串口通信,單片機可以將采集到的數(shù)據(jù)發(fā)送給計算機進行進一步的處理和分析,也可以接收計算機發(fā)送的控制指令,實現(xiàn)遠程控制和自動化測試。

          作為互聯(lián)網(wǎng)行業(yè)工作者,深切建議大家認(rèn)真學(xué)習(xí)并嘗試涉及單片機的學(xué)習(xí),我整理了一些資料大家可以了解下:

          https://m.hqyjai.net/emb_study_blue_short.html?xt=cpx

          在進行單片機與LabVIEW串口通信時,需要注意以下幾個方面:

          1. 串口設(shè)置:首先需要正確配置串口的參數(shù),包括波特率、數(shù)據(jù)位、停止位、校驗位等。這些參數(shù)的設(shè)置必須與單片機端的串口設(shè)置保持一致,以確保數(shù)據(jù)的正確傳輸。

          2. 數(shù)據(jù)格式:單片機發(fā)送的數(shù)據(jù)需要按照一定的格式進行編碼,以便計算機能夠正確解析。常見的數(shù)據(jù)格式包括文本格式和二進制格式。在發(fā)送數(shù)據(jù)時,單片機需要按照約定的格式將數(shù)據(jù)打包成數(shù)據(jù)包,并在數(shù)據(jù)包中添加必要的頭信息和校驗碼,以確保數(shù)據(jù)的完整性和正確性。

          3. 通信協(xié)議:單片機與計算機之間的通信需要遵循一定的通信協(xié)議,以確保數(shù)據(jù)的正確傳輸和解析。常見的通信協(xié)議包括ASCII協(xié)議和Modbus協(xié)議等。選擇合適的通信協(xié)議可以提高數(shù)據(jù)傳輸?shù)目煽啃院头€(wěn)定性。

          4. 錯誤處理:在串口通信過程中,可能會出現(xiàn)各種錯誤,如數(shù)據(jù)丟失、傳輸錯誤等。為了保證數(shù)據(jù)的正確性和可靠性,需要采取一定的錯誤處理措施,如數(shù)據(jù)校驗、重傳機制等。

          總之,單片機與LabVIEW之間的串口通信是一種重要的數(shù)據(jù)傳輸方式,它可以實現(xiàn)單片機與計算機之間的實時數(shù)據(jù)交換和控制。在實際應(yīng)用中,需要根據(jù)具體的應(yīng)用場景和需求,選擇合適的通信協(xié)議和數(shù)據(jù)處理方式,確保數(shù)據(jù)的正確性和可靠性。同時,也需要不斷學(xué)習(xí)和實踐,提高自己的技能水平和應(yīng)用能力。

          https://m.hqyjai.net/emb_study_blue_short.html?xt=cpx#如何學(xué)習(xí)單片機# #什么叫單片機#


          主站蜘蛛池模板: 精品国产免费一区二区三区| 日韩精品一区二区三区中文3d| 国产一区二区在线| 成人在线视频一区| 少妇无码一区二区三区免费| 国产一区精品视频| 久久一区二区三区精华液使用方法| 国产精华液一区二区区别大吗| 国产AV一区二区精品凹凸| 精品动漫一区二区无遮挡| 亚洲AV无码一区二区三区系列| 一区二区三区免费看| 在线日韩麻豆一区| 在线观看视频一区二区| 国产香蕉一区二区在线网站| 亚洲国产一区在线观看| 久久se精品动漫一区二区三区| 国产福利电影一区二区三区,日韩伦理电影在线福 | 久久综合精品不卡一区二区| 婷婷国产成人精品一区二| 影院成人区精品一区二区婷婷丽春院影视| 国产成人一区二区三区视频免费| 久久一区二区三区免费| 久久一区二区三区99| 亚洲一区二区女搞男| 中文字幕一区二区三区乱码| 日本一区二区三区高清| 中文字幕不卡一区| 国产成人无码AV一区二区在线观看| 久久精品中文字幕一区| 久久青草国产精品一区| 无码乱人伦一区二区亚洲一| 色狠狠一区二区三区香蕉| 亚洲区精品久久一区二区三区| 2020天堂中文字幕一区在线观 | 国产日韩一区二区三区| 亚洲色无码专区一区| 一区二区三区美女视频| 国产美女口爆吞精一区二区| 精品福利一区二区三| 无码人妻精品一区二区三区99性|