STM32 F103ZET6包含多個UART、USART串口。
USART,全稱:Universal Synchronous/Asynchronous Receiver/Transmitter,是通用同步/異步串行接收/發(fā)送器,主要特點有:
UART,全稱:Universal Asynchronous Receiver/Transmitter,是通用異步收發(fā)器,在USART功能的基礎(chǔ)上,裁剪掉了同步通信功能,其主要特點:
STM32 F103ZET6芯片,有5個USART接口,數(shù)據(jù)手冊可在官網(wǎng)查詢:
https://www.st.com/zh/microcontrollers-microprocessors/stm32f103.html
根據(jù)手冊的描述:
STM32F103xC、STM32F103xD和STM32F103xE性能型系列集成了:
這五個接口提供了異步通信、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ā)板原理圖:
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功能。
在《stm3210x參考手冊.pdf》P309可以看到STM32的USART框圖:
這里簡單列出常用的USART寄存器,詳細使用方法可以參考《stm32中文參考手冊.pdf》。
用于存儲USART的狀態(tài)信息,包括發(fā)送完成、接收緩沖區(qū)非空、校驗錯誤等。
用于存儲發(fā)送和接收的數(shù)據(jù)。寫入此寄存器可以啟動數(shù)據(jù)發(fā)送,讀取此寄存器可以獲取接收到的數(shù)據(jù)。
用于設(shè)置USART的波特率,通常需要根據(jù)系統(tǒng)時鐘和所需的波特率進行配置。
用于配置USART的工作模式、數(shù)據(jù)格式、中斷使能等。
用于配置USART的硬件流控、時鐘極性等特性。
用于配置USART的其他特性,如DMA使能、多主機模式等。
用于配置USART的守護時間和時鐘預(yù)分頻器,通常與同步通信相關(guān)。
用于配置USART的中斷使能和中斷標(biāo)志位。
在《STM32中文參考手冊》中,中斷請求表:
在普中-F1開發(fā)板上,提供了 RS-232 母頭,其線序:
可以使用一根 RS-232轉(zhuǎn)TTL轉(zhuǎn)USB的連接線,連接USB接電腦,電腦上使用串口調(diào)試工具進行開發(fā)實驗。
RS-232公頭線序:.
配置USART的步驟如下:
首先需要使能USART所使用的時鐘,確保其正常工作。USART掛接的系統(tǒng)總線:
代碼示例:
// 使能 GPIOA 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 使能 USART1 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
配置相關(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;
配置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);
}
最后需要使能USART模塊,開始其正常工作。這涉及到設(shè)置相應(yīng)的控制寄存器來啟動USART的發(fā)送和接收功能。
void USART_Cmd(USAR_TypeDef* USARTx, FunctionalState NewState);
USART_Cmd(USART1, ENABLE);
本章使用使用到串口中斷,關(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);
NVIC_Init()
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)的功能是:
通過電腦串口給開發(fā)板的USART3發(fā)送數(shù)據(jù)0-9的數(shù)據(jù),開發(fā)板的數(shù)碼管顯示對應(yīng)的數(shù)值,并且回復(fù)同樣的內(nèi)容給開發(fā)板。
#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);
}
#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
#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的重定向 ,可以實現(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)衡利弊。
示例代碼里,使用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í)單片機# #什么叫單片機#
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。