在做項(xiàng)目的時(shí)候是不是發(fā)現(xiàn)串口是最常用的通信接口,所以我自己整理了一下關(guān)于串口的一些常識(shí)性知識(shí),方便自己學(xué)習(xí)。
我們常說的串口按電平分類的話有兩種,一種是TTL電平的串口,一種是232電平的串口。 首先我們先看一下這兩種電平的區(qū)別。 TTL電平標(biāo)準(zhǔn):輸出L: <0.8V; H: >2.4V,輸入L: <1.2V: H: >2.0V TTL器件輸出低電平要小于0.8V,高電平要大于2.4V,輸入,低于1.2V就認(rèn)為是0,高于2.0就認(rèn)為是1.于是TTL電平的輸入低電平的噪聲容限就只有(O.8-0)/2=0.4V,高電平的噪聲容限為(5-2.4)/2=1.3V. RS232標(biāo)準(zhǔn): 邏輯1的電平為-3~-15V,邏輯0的電平為+3~+15V,注意電平的定義反相了一次。
然而TTL電平的串口還可以分為兩種UART與USART。我們打開stm32cubeMX看到stm32F103有2個(gè)UART和3個(gè)USART。在日常我們就可以說,它有5個(gè)串口,因?yàn)?/font>USART可以配置成UART。
101643jiorm0w5w3wsswk5.png (54.54 KB, 下載次數(shù): 67)
下載附件
2018-10-20 00:53 上傳
102320u9rmdbd9ibg59td1.png (88.46 KB, 下載次數(shù): 58)
下載附件
2018-10-20 00:53 上傳
UART與USART都是單片機(jī)上的串口通信,他們之間的區(qū)別如下: 首先從名字上看: UART:universal asynchronous receiver and transmitter通用異步收/發(fā)器 USART:universal synchronous asynchronous receiver and transmitter通用同步/異步收/發(fā)器 從名字上可以看出,USART在UART基礎(chǔ)上增加了同步功能,即USART是UART的增強(qiáng)型,事實(shí)也確實(shí)是這樣。但是具體增強(qiáng)到了什么地方呢? 其實(shí)當(dāng)我們使用USART在異步通信的時(shí)候,它與UART沒有什么區(qū)別,但是用在同步通信的時(shí)候,區(qū)別就很明顯了:大家都知道同步通信需要時(shí)鐘來觸發(fā)數(shù)據(jù)傳輸,也就是說USART相對(duì)UART的區(qū)別之一就是能提供主動(dòng)時(shí)鐘。如STM32的USART可以提供時(shí)鐘支持ISO7816的智能卡接口。
在實(shí)際的項(xiàng)目中用的最多的還是UART ,配置為:N, 8 ,1.波特率常用的有2400,9600,57600,115200.我們用串口來干什么呢?用過一些模塊的工程師會(huì)知道,很多模塊都是通過串口來控制或者通過串口透?jìng)鲾?shù)據(jù)的,比如gsm(2g,4g),gps,wifi,藍(lán)牙等等,這些模塊都是通過串口來控制。所以作為一個(gè)工程師,必須要搞懂串口。下面我們通過Loto虛擬示波器采集不同波特率的UART 波形:
102801w029yrdbre2p286y.gif (1.42 MB, 下載次數(shù): 65)
下載附件
2018-10-20 00:53 上傳
102648ckbjnbjizog0dqll.gif (1.28 MB, 下載次數(shù): 67)
下載附件
2018-10-20 00:53 上傳
102627w45chbr7tr4z2040.gif (1.04 MB, 下載次數(shù): 63)
下載附件
2018-10-20 00:53 上傳
100755m6owzlbk6k8nbeko.gif (1.93 MB, 下載次數(shù): 51)
下載附件
2018-10-20 00:53 上傳
對(duì)比理論的時(shí)序圖,我們找找起始位和停止位,這個(gè)數(shù)據(jù)是多少?
084245ilctwumcaxteme0j.png (5.63 KB, 下載次數(shù): 55)
下載附件
2018-10-20 00:53 上傳
有時(shí)候我們面試會(huì)考一個(gè)題目,那就是UART波特率9600的時(shí)候,它的一個(gè)bit是多長(zhǎng)時(shí)間,這個(gè)怎么算?初學(xué)者肯定蒙了,我用串口直接就配置然后發(fā)數(shù)據(jù)就行了,從來沒算過,下面我總結(jié)一下:
波特率2400:1/2400≈416.67*10^(-6)s=416.67us 波特率4800:1/4800≈208.33*10^(-6)s=208.33us 波特率9600:1/9600≈104.17*10^(-6)s=104.17us 波特率19200:1/19200≈52.08*10^(-6)s=52.08us 波特率57600:1/57600≈17.36*10^(-6)s=17.36us 波特率115200:1/115200≈8.68*10^(-6)s=8.68us
對(duì)比我們用Loto虛擬示波器采集的數(shù)據(jù)看看是不是一致呢?
112534r9rn9wr8ruw28n7u.png (214.25 KB, 下載次數(shù): 54)
下載附件
2018-10-20 00:53 上傳
112615lf27ss0p4432w6pp.png (204.86 KB, 下載次數(shù): 58)
下載附件
2018-10-20 00:53 上傳
112633qdnjjd288jmt8dd8.png (211.7 KB, 下載次數(shù): 62)
下載附件
2018-10-20 00:53 上傳
112643ujq2q7bpoqgqe55e.png (208.28 KB, 下載次數(shù): 54)
下載附件
2018-10-20 00:53 上傳
我們算這個(gè)數(shù)據(jù)有什么用呢?是的!沒錯(cuò)!就是模擬串口!當(dāng)單片機(jī)串口不夠用的時(shí)候項(xiàng)目中會(huì)用到。接下來我會(huì)分析幾個(gè)論友寫的代碼,逐步深入理解TTL串口。
下面這個(gè)模擬串口的代碼: 首先說它的發(fā)送吧,發(fā)送一個(gè)字節(jié),先發(fā)送起始位OI_TXD = 0; 然后delay_us(BuadRate_9600);然后依次從低位開始檢測(cè)Data的每一位,從而拉高拉低OI_TXD,然后延時(shí),移位,重復(fù)8次,最后發(fā)送停止位發(fā)送完一個(gè)Data。 接收的話:它是stm32開了個(gè)定時(shí)器4和一個(gè)外部中斷.外部中斷采到下降沿,然后定時(shí)器設(shè)置為107us中斷一次,去采集一個(gè)bit,為什么是107us呢?因?yàn)榇藭r(shí)波特率是9600,一個(gè)bit是104.17us,然后可以依次采集每個(gè)bit,直到采完一個(gè)data。(個(gè)人感覺107us不穩(wěn)定)
- /**
- *軟件串口的實(shí)現(xiàn)(IO模擬串口)
- * 波特率:9600 1-8-N
- * TXD : PC13
- * RXD : PB14
- * 使用外部中斷對(duì)RXD的下降沿進(jìn)行觸發(fā),使用定時(shí)器4按照9600波特率進(jìn)行定時(shí)數(shù)據(jù)接收。
- * Demo功能: 接收11個(gè)數(shù)據(jù),然后把接收到的數(shù)據(jù)發(fā)送出去
- */
- #define OI_TXD PCout(13)
- #define OI_RXD PBin(14)
- #define BuadRate_9600 100
- u8 len = 0; //接收計(jì)數(shù)
- u8 USART_buf[11]; //接收緩沖區(qū)
- enum{
- COM_START_BIT,
- COM_D0_BIT,
- COM_D1_BIT,
- COM_D2_BIT,
- COM_D3_BIT,
- COM_D4_BIT,
- COM_D5_BIT,
- COM_D6_BIT,
- COM_D7_BIT,
- COM_STOP_BIT,
- };
- u8 recvStat = COM_STOP_BIT;
- u8 recvData = 0;
- void IO_TXD(u8 Data)
- {
- u8 i = 0;
- OI_TXD = 0;
- delay_us(BuadRate_9600);
- for(i = 0; i < 8; i++)
- {
- if(Data&0x01)
- OI_TXD = 1;
- else
- OI_TXD = 0;
-
- delay_us(BuadRate_9600);
- Data = Data>>1;
- }
- OI_TXD = 1;
- delay_us(BuadRate_9600);
- }
-
- void USART_Send(u8 *buf, u8 len)
- {
- u8 t;
- for(t = 0; t < len; t++)
- {
- IO_TXD(buf[t]);
- }
- }
-
- void IOConfig(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- EXTI_InitTypeDef EXTI_InitStruct;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口時(shí)鐘
-
- //SoftWare Serial TXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- GPIO_SetBits(GPIOC,GPIO_Pin_13);
-
-
- //SoftWare Serial RXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOB, &GPIO_InitStructure);
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
- EXTI_InitStruct.EXTI_Line = EXTI_Line14;
- EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
- EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿觸發(fā)中斷
- EXTI_InitStruct.EXTI_LineCmd=ENABLE;
- EXTI_Init(&EXTI_InitStruct);
- NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- }
-
- void TIM4_Int_Init(u16 arr,u16 psc)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //時(shí)鐘使能
-
- //定時(shí)器TIM4初始化
- TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)置在下一個(gè)更新事件裝入活動(dòng)的自動(dòng)重裝載寄存器周期的值
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //設(shè)置用來作為TIMx時(shí)鐘頻率除數(shù)的預(yù)分頻值
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設(shè)置時(shí)鐘分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計(jì)數(shù)模式
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根據(jù)指定的參數(shù)初始化TIMx的時(shí)間基數(shù)單位
- TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
- TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,允許更新中斷
- //中斷優(yōu)先級(jí)NVIC設(shè)置
- NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中斷
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占優(yōu)先級(jí)1級(jí)
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //從優(yōu)先級(jí)1級(jí)
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
- NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
- }
-
-
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級(jí)分組為組2:2位搶占優(yōu)先級(jí),2位響應(yīng)優(yōu)先級(jí)
- delay_init();
- IOConfig();
- TIM4_Int_Init(107, 71); //1M計(jì)數(shù)頻率
-
- while(1)
- {
- if(len > 10)
- {
- len = 0;
- USART_Send(USART_buf,11);
- }
- }
- }
- void EXTI15_10_IRQHandler(void)
- {
- if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)
- {
- if(OI_RXD == 0)
- {
- if(recvStat == COM_STOP_BIT)
- {
- recvStat = COM_START_BIT;
- TIM_Cmd(TIM4, ENABLE);
- }
- }
- EXTI_ClearITPendingBit(EXTI_Line14);
- }
- }
- void TIM4_IRQHandler(void)
- {
- if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)
- {
- TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
- recvStat++;
- if(recvStat == COM_STOP_BIT)
- {
- TIM_Cmd(TIM4, DISABLE);
- USART_buf[len++] = recvData;
- return;
- }
- if(OI_RXD)
- {
- recvData |= (1 << (recvStat - 1));
- }else{
- recvData &= ~(1 << (recvStat - 1));
- }
- }
- }
復(fù)制代碼
還有一個(gè)例子- #include "stm32f10x.h"
- #include "sys.h"
- #include "exti.h"
- #include "stdio.h"
- //加入以下代碼,支持printf函數(shù),而不需要選擇use MicroLIB
- #if 1
- #pragma import(__use_no_semihosting)
- //標(biāo)準(zhǔn)庫需要的支持函數(shù)
- struct __FILE
- {
- int handle;
- };
- FILE __stdout;
- //定義_sys_exit()以避免使用半主機(jī)模式
- _sys_exit(int x)
- {
- x = x;
- }
- //重定義fputc函數(shù)
- int fputc(int ch, FILE *f)
- {
- void SendOneByte(u8 Byte);
- SendOneByte((uint8_t) ch);
- return ch;
- }
- #endif
- void SendOneByte(u8 Byte)
- {
- u8 i=8,tmp;
- TXD_low(); //發(fā)送起始位
- delay_us(104);
- //發(fā)送8位數(shù)據(jù)
- for(i=0;i<8;i++)
- {
- tmp = (Byte >> i) & 0x01; //低位在前
- if(tmp == 0)
- {
- TXD_low();
- delay_us(104); //0
- }
- else
- {
- TXD_high();
- delay_us(104); //1
- }
- }
- // while(i--)
- // {
- // MNUSART_TXD = (Byte&0x01); //先傳低位
- // delay_us(104);
- // Byte = Byte>>1;
- // //無校驗(yàn)位
- // MNUSART_TXD=1;//發(fā)送結(jié)束位
- // delay_us(104);
-
- // }
- TXD_high();
- delay_us(104);
- }
- void SendBytes(u8 *str,u8 len) //發(fā)送數(shù)組最好用這個(gè),也可發(fā)送字符串
- {
- u16 i;
- for(i=0;i<len;i++)
- {
- SendOneByte(str[i]);
-
- }
- }
- void SendStr(u8 *str) //發(fā)送字符串,發(fā)送數(shù)組如果帶0x00就中斷發(fā)送了
- {
- while(*str)
- SendOneByte(*str++);
- }
- int main(void)
- {
- u8 len;
- u8 hello[]={0x5a,0xa5,0x00,0x00,0x01};
- delay_init();
- EXTIX_Init();
- //測(cè)試 發(fā)送一個(gè)字節(jié)
- SendOneByte(0x00);
- SendOneByte(0x01);
- SendOneByte(0x02);
- //測(cè)試發(fā)送數(shù)組
- SendBytes(hello,5);
- //測(cè)試發(fā)送字符串
- // SendBytes("hello word",11);
- // SendStr("德瑪西亞");
-
- while(1)
- {
- if(USART_RX_STA&0x8000)
- {
- printf("您發(fā)送的消息為:");
- len=USART_RX_STA&0x3fff;//得到此次接收到的數(shù)據(jù)長(zhǎng)度
- SendBytes(USART_RX_BUF,len);
- //也可以用下面的發(fā)送
- // SendStr(USART_RX_BUF);
- // for(len=0;len<200;len++)
- // USART_RX_BUF[len]=0;
- printf("");//插入換行
- USART_RX_STA=0;
- }
- }
- }
復(fù)制代碼
- #include "exti.h"
- #include "stm32f10x_gpio.h"
- #include "stm32f10x_exti.h"
- #include "sys.h"
- u8 tmp;
- u8 USART_RX_BUF[USART_REC_LEN];
- u16 USART_RX_STA=0;
- //extern SendOneByte(u8 Byte);
- void EXTIX_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- EXTI_InitTypeDef EXTI_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 端口配置
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
- GPIO_Init(GPIOA, &GPIO_InitStructure); //根據(jù)設(shè)定參數(shù)初始化GPIOB.5
- GPIO_SetBits(GPIOA,GPIO_Pin_9);
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空輸入
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- GPIO_SetBits(GPIOA,GPIO_Pin_10);
- //GPIOA.10 中斷線以及中斷初始化配置 下降沿觸發(fā)
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource10);
- EXTI_InitStructure.EXTI_Line=EXTI_Line10;
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
- EXTI_Init(&EXTI_InitStructure); //根據(jù)EXTI_InitStruct中指定的參數(shù)初始化外設(shè)EXTI寄存器
- NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- }
- void EXTI15_10_IRQHandler(void)
- {
- u8 i=9;
- if(EXTI_GetITStatus(EXTI_Line10) != RESET)
- {
- /* Disable the Selected IRQ Channels -------------------------------------*/
- NVIC->ICER[EXTI15_10_IRQn >> 0x05] =
- (uint32_t)0x01 << (EXTI15_10_IRQn & (uint8_t)0x1F);
- EXTI_ClearITPendingBit(EXTI_Line10);
- delay_us(30);
- while(i--)
- {
- tmp >>=1;
- if(GPIOA->IDR&0x0400) tmp |=0x80;
- delay_us(104);
- }
- if((USART_RX_STA&0x8000)==0)
- {
- if(USART_RX_STA&0x4000)
- {
- if(tmp!=0x0a) USART_RX_STA=0;
- else USART_RX_STA |=0x8000;
- }
- else
- {
- if(tmp==0x0d) USART_RX_STA|=0x4000;
- else
- {
- USART_RX_BUF[USART_RX_STA&0X3FFF]=tmp ;
- USART_RX_STA++;
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數(shù)據(jù)錯(cuò)誤,重新開始接收
- }
- }
-
- }
- EXTI_ClearITPendingBit(EXTI_Line10);
- NVIC->ISER[EXTI15_10_IRQn >> 0x05] =
- (uint32_t)0x01 << (EXTI15_10_IRQn & (uint8_t)0x1F);
- }
- }
復(fù)制代碼 |