在項(xiàng)目中需要用到多路的串口使用,而自己的單片機(jī)目前來講沒法滿足我們項(xiàng)目所需要的串口需求,因此要對普通的GPIO進(jìn)行轉(zhuǎn)換為UART進(jìn)行使用。從而使得我們單片機(jī)能夠得到多一路的串口。
既然我們要進(jìn)行對串口的模擬,因此我們要先了解uart相關(guān)的通信協(xié)議:帶你快速對比SPI、UART、I2C通信的區(qū)別與應(yīng)用!,這篇文章進(jìn)行了詳細(xì)介紹,此處不再贅述。
UART的通信方式是由1個(gè)起始位,8個(gè)數(shù)據(jù)位,包含一個(gè)奇偶校驗(yàn)位,和結(jié)束位構(gòu)成。因此我們將使用單片機(jī)中的兩個(gè)普通的IO口電平的高低進(jìn)行對相應(yīng)時(shí)序的模擬。
接下來,讓我們一起學(xué)習(xí)如何實(shí)現(xiàn)IO模擬串口通信。
添加頭文件
首先我們先添加相應(yīng)的頭文件。
#include "stm32f10x.h" #include "vuart2.h"
宏定義
使用到的io口為:
#define OI2_TXD PDout(6) #define OI2_RXD PDin(7) #define BuadRate2_9600 104 #define Recive2_Byte 19 //接收緩沖器的個(gè)數(shù) u8 len2 = 0; //接收計(jì)數(shù) u8 USART2_buf[Recive2_Byte]; //接收緩沖區(qū)
將IO口相應(yīng)的位帶操作函數(shù)進(jìn)行宏定義從而使得在對不同的電平的進(jìn)行轉(zhuǎn)換的時(shí)候更為方便,并且減少了調(diào)用其他函數(shù)的過程所消耗的時(shí)間,程序執(zhí)行效率更高。
在本次的傳輸過程中我選用的是使用波特率速率為9600bps,也就是1s中發(fā)送9600個(gè)數(shù)據(jù)位(bit),因此對每個(gè)位數(shù)據(jù)進(jìn)行計(jì)算1000000us/9600可以得出,發(fā)一個(gè)bit的數(shù)據(jù)需要進(jìn)行大概需要 104.16us,并且對于相應(yīng)的電平持續(xù)時(shí)間要求誤差不能超過±5%,因此對我們進(jìn)行時(shí)間的控制要求就顯得比較重要了。
枚舉出各個(gè)位
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;
IO——TXD進(jìn)行模擬
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); }
由于發(fā)送的信號是將TXD信號進(jìn)行拉低處理,因此在拉低TXD相應(yīng)的IO口之后進(jìn)行延時(shí)處理,再進(jìn)行循環(huán)對我們需要發(fā)送的各個(gè)位的數(shù)據(jù)繼續(xù)進(jìn)行發(fā)送循環(huán)發(fā)送完成之后將電平拉高代表停止位。
構(gòu)建發(fā)送函數(shù)
void USART2_Send(u8 *buf, u8 len2) { u8 t; for(t = 0; t < len2; t++) { IO2_TXD(buf[t]); } }
其中的*buf為需要發(fā)送的數(shù)據(jù),len2為數(shù)據(jù)長度,進(jìn)行循環(huán)調(diào)用IO_TXD進(jìn)行一個(gè)字節(jié)一個(gè)字節(jié)的數(shù)據(jù)發(fā)送。
IO口初始化
void IO2Config(void) { GPIO_InitTypeDef GPIO_InitStructure;//初始化gpio NVIC_InitTypeDef NVIC_InitStructure;//中斷初始化函數(shù) EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE); //使能PD,PC端口時(shí)鐘 //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默認(rèn)電平拉高 //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的下降沿進(jìn)行中斷采樣,當(dāng)接收到下降沿時(shí)代表接收到數(shù)據(jù)觸發(fā)中斷處理函數(shù) EXTI_InitStruct.EXTI_Line = EXTI_Line7;//用到了中斷7 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= EXTI9_5_IRQn ; //中斷發(fā)生于9-5的中斷之中 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); }
定時(shí)器初始化
void TIM5_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //時(shí)鐘使能 //定時(shí)器TIM5初始化 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(TIM5, &TIM_TimeBaseStructure); //根據(jù)指定的參數(shù)初始化TIMx的時(shí)間基數(shù)單位 TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中斷,允許更新中斷 //中斷優(yōu)先級NVIC設(shè)置 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM5中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占優(yōu)先級1級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //從優(yōu)先級1級 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 }
對TIM5進(jìn)行初始化操作使得定時(shí)器可以檢測到各個(gè)位的電平持續(xù)性時(shí)間從而對接收到的數(shù)據(jù)進(jìn)行分析。計(jì)時(shí)結(jié)束后進(jìn)入中斷TIM5處理。
外部中斷處理函數(shù)
void EXTI9_5_IRQHandler(void) { if(EXTI_GetFlagStatus(EXTI_Line7) != RESET)//對中斷標(biāo)志位進(jìn)行采集 { if(OI2_RXD == 0) { if(recvStat2 == COM_STOP_BIT) { recvStat2 = COM_START_BIT;//將當(dāng)前的狀態(tài)設(shè)置為開始位 TIM_Cmd(TIM5, ENABLE);//開啟定時(shí)器計(jì)數(shù) } } EXTI_ClearITPendingBit(EXTI_Line7); //清除中斷標(biāo)志 } }
定時(shí)器中斷處理函數(shù)
void TIM5_IRQHandler(void) { if(TIM_GetFlagStatus(TIM5, TIM_FLAG_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); //清除中斷標(biāo)志位 recvStat2++; //將位置移動(dòng)到第一位的數(shù)據(jù) if(recvStat2 == COM_STOP_BIT)//當(dāng)運(yùn)行到停止位時(shí)進(jìn)入 { TIM_Cmd(TIM5, DISABLE);//停止tim5 USART2_buf[len2++] = recvData2;//將采集到的各個(gè)數(shù)據(jù)傳遞給USART2_buf if(len2 > Recive2_Byte-1)//將數(shù)據(jù)通過回顯到串口調(diào)試助手中 { len2 = 0; USART2_Send(USART2_buf,Recive2_Byte); } return; } if(OI2_RXD)//采集RXD各個(gè)電平 { recvData2 |= (1 << (recvStat2 - 1)); }else{ recvData2 &= ~(1 << (recvStat2 - 1)); } } }
整體代碼
vuart2.c:
#include "stm32f10x.h" #include "vuart2.h" /** *軟件串口的實(shí)現(xiàn)(IO模擬串口) * 波特率:9600 1-8-N * TXD : PD6 * RXD : PD7 * 使用外部中斷對RXD的下降沿進(jìn)行觸發(fā),使用定時(shí)器5按照9600波特率進(jìn)行定時(shí)數(shù)據(jù)接收。 * Demo功能: 接收11個(gè)數(shù)據(jù),然后把接收到的數(shù)據(jù)發(fā)送出去 */ #define OI2_TXD PDout(6) #define OI2_RXD PDin(7) #define BuadRate2_9600 104 #define Recive2_Byte 19 //接收緩沖器的個(gè)數(shù) u8 len2 = 0; //接收計(jì)數(shù) u8 USART2_buf[Recive2_Byte]; //接收緩沖區(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 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 < len2; 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端口時(shí)鐘 //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; //下降沿觸發(fā)中斷 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); //時(shí)鐘使能 //定時(shí)器TIM5初始化 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(TIM5, &TIM_TimeBaseStructure); //根據(jù)指定的參數(shù)初始化TIMx的時(shí)間基數(shù)單位 TIM_ClearITPendingBit(TIM5, TIM_FLAG_Update); TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中斷,允許更新中斷 //中斷優(yōu)先級NVIC設(shè)置 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM4中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占優(yōu)先級1級 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //從優(yōu)先級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)); } } }
vuart2.h:
#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
審核編輯:劉清
-
單片機(jī)
+關(guān)注
關(guān)注
6035文章
44554瀏覽量
634621 -
串口通信
+關(guān)注
關(guān)注
34文章
1624瀏覽量
55507 -
GPIO
+關(guān)注
關(guān)注
16文章
1204瀏覽量
52051 -
UART接口
+關(guān)注
關(guān)注
0文章
124瀏覽量
15288
原文標(biāo)題:在STM32中如何通過IO口模擬串口通信
文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論