1、添加頭文件
首先我們先添加相應的頭文件。既然我們要進行對串口的模擬,因此我們要先了解uart相關的通信協議。由于UART的通信方式是由1個起始位,8個數據位,包含一個奇偶校驗位,和結束位構成因此我們將使用單片機中的兩個普通的IO口電平的高低進行對相應時序的模擬。

#include"stm32f10x.h" #include"vuart2.h"
2、宏定義
使用到的io口為
#defineOI2_TXDPDout(6) #defineOI2_RXDPDin(7) #defineBuadRate2_9600104 #defineRecive2_Byte19//接收緩沖器的個數 u8len2=0;//接收計數 u8USART2_buf[Recive2_Byte];//接收緩沖區
將IO口相應的位帶操作函數進行宏定義從而使得在對不同的電平的進行轉換的時候更為方便,并且減少了調用其他函數的過程所消耗的時間,程序執行效率更高。
在本次的傳輸過程中我選用的是使用波特率速率為9600,也就是1s中發送9600個字節,因此對每個字節數據進行計算1000000us/9600可以得出,發一個字節的數據需要進行大概需要 104.16us并且對于相應的電平持續時間要求誤差不能超過±5%因此對我們進行時間的控制要求就顯得比較重要了。
3、枚舉出各個位
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,
};
u8recvStat2=COM_STOP_BIT;
u8recvData2=0;
4、IO——TXD進行模擬
voidIO2_TXD(u8Data)
{
u8i=0;
OI2_TXD=0;
delay_us(BuadRate2_9600);
for(i=0;i8;?i++)
????{
????????if(Data&0x01)
????????????OI2_TXD?=?1;??
????????else
????????????OI2_TXD?=?0;??
????????
????????delay_us(BuadRate2_9600);
????????Data?=?Data>>1;
}
OI2_TXD=1;
delay_us(BuadRate2_9600);
}
由于發送的信號是將TXD信號進行拉低處理,因此在拉低TXD相應的IO口之后進行延時處理,再進行循環對我們需要發送的各個位的數據繼續進行發送循環發送完成之后將電平拉高代表停止位。
5、構建發送函數
voidUSART2_Send(u8*buf,u8len2)
{
u8t;
for(t=0;t
其中的*buf為需要發送的數據,len2為數據長度,進行循環調用IO_TXD進行一個字節一個字節的數據發送。
6、IO口初始化
voidIO2Config(void)
{
GPIO_InitTypeDefGPIO_InitStructure;//初始化gpio
NVIC_InitTypeDefNVIC_InitStructure;//中斷初始化函數
EXTI_InitTypeDefEXTI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,ENABLE);//使能PD,PC端口時鐘
//SoftWareSerialTXD
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//選擇io口6
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽輸出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度為50MHz
GPIO_Init(GPIOD,&GPIO_InitStructure);
GPIO_SetBits(GPIOD,GPIO_Pin_6);//TXD默認電平拉高
//SoftWareSerialRXD
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOD,&GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource7);//對D7的下降沿進行中斷采樣,當接收到下降沿時代表接收到數據觸發中斷處理函數
EXTI_InitStruct.EXTI_Line=EXTI_Line7;//用到了中斷7
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿觸發中斷
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStruct);//初始化中斷
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;//中斷發生于9-5的中斷之中
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
7、定時器初始化
voidTIM5_Int_Init(u16arr,u16psc)
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
NVIC_InitTypeDefNVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//時鐘使能
//定時器TIM5初始化
TIM_TimeBaseStructure.TIM_Period=arr;//設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler=psc;//設置用來作為TIMx時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//設置時鐘分割:TDTS=Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上計數模式
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);//根據指定的參數初始化TIMx的時間基數單位
TIM_ClearITPendingBit(TIM5,TIM_FLAG_Update);
TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//使能指定的TIM5中斷,允許更新中斷
//中斷優先級NVIC設置
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;//TIM5中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//先占優先級1級
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//從優先級1級
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道被使能
NVIC_Init(&NVIC_InitStructure);//初始化NVIC寄存器
}
對TIM5進行初始化操作使得定時器可以檢測到各個位的電平持續性時間從而對接收到的數據進行分析。計時結束后進入中斷TIM5處理。
8、外部中斷處理函數
voidEXTI9_5_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line7)!=RESET)//對中斷標志位進行采集
{
if(OI2_RXD==0)
{
if(recvStat2==COM_STOP_BIT)
{
recvStat2=COM_START_BIT;//將當前的狀態設置為開始位
TIM_Cmd(TIM5,ENABLE);//開啟定時器計數
}
}
EXTI_ClearITPendingBit(EXTI_Line7);//清除中斷標志
}
}
9、定時器中斷處理函數
voidTIM5_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM5,TIM_FLAG_Update)!=RESET)
{
TIM_ClearITPendingBit(TIM5,TIM_FLAG_Update);//清除中斷標志位
recvStat2++;//將位置移動到第一位的數據
if(recvStat2==COM_STOP_BIT)//當運行到停止位時進入
{
TIM_Cmd(TIM5,DISABLE);//停止tim5
USART2_buf[len2++]=recvData2;//將采集到的各個數據傳遞給USART2_buf
if(len2>Recive2_Byte-1)//將數據通過回顯到串口調試助手中
{
len2=0;
USART2_Send(USART2_buf,Recive2_Byte);
}
return;
}
if(OI2_RXD)//采集RXD各個電平
{
recvData2|=(1<(recvStat2?-?1));
????????}else{
????????????recvData2?&=?~(1?<(recvStat2?-?1));
????????}?
??}??
}
整體代碼
vuart2.c
#include"stm32f10x.h"
#include"vuart2.h"
/**
*軟件串口的實現(IO模擬串口)
*波特率:9600 1-8-N
*TXD:PD6
*RXD:PD7
*使用外部中斷對RXD的下降沿進行觸發,使用定時器5按照9600波特率進行定時數據接收。
*Demo功能:接收11個數據,然后把接收到的數據發送出去
*/
#defineOI2_TXDPDout(6)
#defineOI2_RXDPDin(7)
#defineBuadRate2_9600104
#defineRecive2_Byte19//接收緩沖器的個數
u8len2=0;//接收計數
u8USART2_buf[Recive2_Byte];//接收緩沖區
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,
};
u8recvStat2=COM_STOP_BIT;
u8recvData2=0;
voidIO2_TXD(u8Data)
{
u8i=0;
OI2_TXD=0;
delay_us(BuadRate2_9600);
for(i=0;i8;?i++)
????{
????????if(Data&0x01)
????????????OI2_TXD?=?1;??
????????else
????????????OI2_TXD?=?0;??
????????
????????delay_us(BuadRate2_9600);
????????Data?=?Data>>1;
}
OI2_TXD=1;
delay_us(BuadRate2_9600);
}
voidUSART2_Send(u8*buf,u8len2)
{
u8t;
for(t=0;tRecive2_Byte-1)
{
len2=0;
USART2_Send(USART2_buf,Recive2_Byte);
}
return;
}
if(OI2_RXD)
{
recvData2|=(1<(recvStat2?-?1));
????????}else{
????????????recvData2?&=?~(1?<(recvStat2?-?1));
????????}?
??}??
}
vuart2.h
#ifndef__VUART2__H
#define__VUART2__H
#include"stm32f10x.h"
voidIO2_TXD(u8Data);
voidUSART2_Send(u8*buf,u8len);
voidIO2Config(void);
voidTIM5_Int_Init(u16arr,u16psc);
#endif
審核編輯:湯梓紅
-
單片機
+關注
關注
6076文章
45494瀏覽量
670245 -
通信
+關注
關注
18文章
6391瀏覽量
140037 -
STM32
+關注
關注
2309文章
11162瀏覽量
373399 -
串口
+關注
關注
15文章
1618瀏覽量
82803 -
uart
+關注
關注
22文章
1314瀏覽量
106634
原文標題:STM32 模擬串口(UART)使用!
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
uart是什么意思?認識uart串口
串口通信的原理,IO口模擬UART串口通信
【CubeIDE】STM32 HAL庫史上最詳細教程(一):UART串口收發
【STM32學習筆記】串口通訊 — USART / UART
基于STM32之UART串口通信協議(一)詳解
stm32使用IO模擬串口
STM32F103VE USART & UART串口通信
STM32 HAL庫 UART 串口讀寫功能筆記
單片機IO口模擬UART串口通信
STM32模擬串口(UART)使用
評論