
在項目中需要用到多路的串口使用,而自己的單片機目前來講沒法滿足我們項目所需要的串口需求,因此要對普通的GPIO進行轉換為UART進行使用。從而使得我們單片機能夠得到多一路的串口。
既然我們要進行對串口的模擬,因此我們要先了解uart相關的通信協議。通過分析波形,透徹理解 UART 通信,這篇文章進行了詳細介紹,此處不再贅述。
UART的通信方式是由1個起始位,8個數據位,包含一個奇偶校驗位,和結束位構成。因此我們將使用單片機中的兩個普通的IO口電平的高低進行對相應時序的模擬。

接下來,讓我們一起學習如何實現IO模擬串口通信。
添加頭文件
首先我們先添加相應的頭文件。
#include?"stm32f10x.h"
#include?"vuart2.h"#define?OI2_TXD?PDout(6)
#define?OI2_RXD?PDin(7)
#define?BuadRate2_9600?104?
#define?Recive2_Byte?19?//接收緩沖器的個數
u8?len2?=?0;?//接收計數
u8?USART2_buf[Recive2_Byte];??//接收緩沖區將IO口相應的位帶操作函數進行宏定義從而使得在對不同的電平的進行轉換的時候更為方便,并且減少了調用其他函數的過程所消耗的時間,程序執行效率更高。
在本次的傳輸過程中我選用的是使用波特率速率為9600,也就是1s中發送9600個字節,因此對每個字節數據進行計算1000000us/9600可以得出,發一個字節的數據需要進行大概需要 104.16us并且對于相應的電平持續時間要求誤差不能超過±5%因此對我們進行時間的控制要求就顯得比較重要了。
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?recvStat2?=?COM_STOP_BIT;
u8?recvData2?=?0;void?IO2_TXD(u8?Data)
{
????u8?i?=?0;
????OI2_TXD?=?0;??
????delay_us(BuadRate2_9600);
????for(i?=?0;?i?8;?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口之后進行延時處理,再進行循環對我們需要發送的各個位的數據繼續進行發送循環發送完成之后將電平拉高代表停止位。
void?USART2_Send(u8?*buf,?u8?len2)
{
????u8?t;
????for(t?=?0;?t?????{
????????IO2_TXD(buf[t]);
????}
}其中的*buf為需要發送的數據,len2為數據長度,進行循環調用IO_TXD進行一個字節一個字節的數據發送。
void?IO2Config(void)
?{
????GPIO_InitTypeDef??GPIO_InitStructure;//初始化gpio
????NVIC_InitTypeDef?NVIC_InitStructure;//中斷初始化函數
?????EXTI_InitTypeDef?EXTI_InitStruct;
????RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,?ENABLE);??//使能PD,PC端口時鐘?
?????
?????//SoftWare?Serial?TXD
????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默認電平拉高
?????
????//SoftWare?Serial?RXD
????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);??
}void?TIM5_Int_Init(u16?arr,u16?psc)
{
????TIM_TimeBaseInitTypeDef??TIM_TimeBaseStructure;
????NVIC_InitTypeDef?NVIC_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處理。
void?EXTI9_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);?//清除中斷標志
????}
}void?TIM5_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));
????????}?
??}??
}#include?"stm32f10x.h"
#include?"vuart2.h"
/**
*軟件串口的實現(IO模擬串口)
*?波特率:9600??? 1-8-N
*?TXD?:?PD6
*?RXD?:?PD7
*?使用外部中斷對RXD的下降沿進行觸發,使用定時器5按照9600波特率進行定時數據接收。
*?Demo功能:?接收11個數據,然后把接收到的數據發送出去
*/
#define?OI2_TXD?PDout(6)
#define?OI2_RXD?PDin(7)
#define?BuadRate2_9600?104?
#define?Recive2_Byte?19?//接收緩沖器的個數
u8?len2?=?0;?//接收計數
u8?USART2_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,
};
u8?recvStat2?=?COM_STOP_BIT;
u8?recvData2?=?0;
void?IO2_TXD(u8?Data)
{
????u8?i?=?0;
????OI2_TXD?=?0;??
????delay_us(BuadRate2_9600);
????for(i?=?0;?i?8;?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);
}
????
void?USART2_Send(u8?*buf,?u8?len2)
{
????u8?t;
????for(t?=?0;?t?????{
????????IO2_TXD(buf[t]);
????}
}
????
?void?IO2Config(void)
?{
????GPIO_InitTypeDef??GPIO_InitStructure;
????NVIC_InitTypeDef?NVIC_InitStructure;
?????EXTI_InitTypeDef?EXTI_InitStruct;
????RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,?ENABLE);??//使能PB,PC端口時鐘?
?????
?????//SoftWare?Serial?TXD
????GPIO_InitStructure.GPIO_Pin?=?GPIO_Pin_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);???????
?????
????//SoftWare?Serial?RXD
????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);
????EXTI_InitStruct.EXTI_Line?=?EXTI_Line7;
????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?;?
????NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;?
????NVIC_InitStructure.NVIC_IRQChannelSubPriority?=2;??
????NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;??
????NVIC_Init(&NVIC_InitStructure);??
}
?
void?TIM5_Int_Init(u16?arr,u16?psc)
{
????TIM_TimeBaseInitTypeDef??TIM_TimeBaseStructure;
????NVIC_InitTypeDef?NVIC_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;??//TIM4中斷
????NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority?=?1;??//先占優先級1級
????NVIC_InitStructure.NVIC_IRQChannelSubPriority?=?1;??//從優先級1級
????NVIC_InitStructure.NVIC_IRQChannelCmd?=?ENABLE;?//IRQ通道被使能
????NVIC_Init(&NVIC_InitStructure);??//初始化NVIC寄存器????
}
void?EXTI9_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);
????}
}
void?TIM5_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);
????????????USART2_buf[len2++]?=?recvData2;
????????if(len2?>?Recive2_Byte-1)
????????{
????????????len2?=?0;
????????????USART2_Send(USART2_buf,Recive2_Byte);
????????}
????????????return;
????????}
????????if(OI2_RXD)????????{
????????????recvData2?|=?(1?<(recvStat2?-?1));
????????}else{
????????????recvData2?&=?~(1?<(recvStat2?-?1));
????????}?
??}??
}
#ifndef?__VUART2__H
#define?__VUART2__H
#include?"stm32f10x.h"
void?IO2_TXD(u8?Data);
void?USART2_Send(u8?*buf,?u8?len);
void?IO2Config(void);
void?TIM5_Int_Init(u16?arr,u16?psc);
#endif原文:https://blog.csdn.net/weixin_48426161/article/details/125905515?spm=1001.2014.3001.5501
文章來源于網絡,版權歸原作者所有,如有侵權,請聯系刪除。


覺得文章不錯,點擊“分享”、“贊”、“在看” 唄!