UART即通用異步收發器,是一種串行通信方式。數據在傳輸過程中是通過一位一位地進行傳輸來實現通信的,串行通信方式具有傳輸線少,成本底等優點,缺點是速度慢。串行通信分為兩種類型:同步通信方式和異步通信方式。 ????但一般多用異步通信方式,主要因為接受和發送的時鐘是可以獨立的這樣有利于增加發送與接收的靈活性。異步通信是一個字符接著一個字符傳輸,一個字符的信息由起始位、數據位、奇偶校驗位和停止位組成。 ????每一個字符的傳輸靠起始位來同步,字符的前面一位是起始位,用下降沿通知收方開始傳輸,緊接著起始位之后的是數據位,傳輸時低位在前高位在后,字符本身由5~8位數據位組成。 ????數據位后面是奇偶校驗位,最后是停止位,停止位是用高電平來標記一個字符的結束,并為下一個字符的傳輸做準備。停止位后面是不同長度的空閑位。停止位和空閑位都規定為高電平,這樣可以保證起始位有一個下降沿。 ????UART的幀格式如圖:
????UART的幀格式包括線路空閑狀態(idle,高電平)、起始位(start bit,低電平)、5~8位數據位(data bits)、校驗位(parity bit,可選)和停止位(stop bit,位數可為1、1.5、2位)。 ????往期相關推文:STM32串口通信基本原理
UART模擬原理
UART的模擬方式基本就是定時器+IO口實現。
方案1:只打印不接收
如果在實際使用中只是為了打印log而不接收數據,可以采用DWT加普通IO口的方式;
#define VCOM_BOUND 115200 #define VCOM_PIN GPIO_Pin_11 #define VCOM_PORT GPIOA #define VCOM_PIN_HIGH VCOM_PORT->BSRR = VCOM_PIN #define VCOM_PIN_LOW VCOM_PORT->BRR = VCOM_PIN #define BSP_REG_DEM_CR (*(volatile unsigned int *)0xE000EDFC) //DEMCR寄存器 #define BSP_REG_DWT_CR (*(volatile unsigned int *)0xE0001000) //DWT控制寄存器 #define BSP_REG_DWT_CYCCNT (*(volatile unsigned int *)0xE0001004) //DWT時鐘計數寄存器 #define BSP_REG_DBGMCU_CR (*(volatile unsigned int *)0xE0042004) #define DEF_BIT_00 0x01u #define DEF_BIT_24 0x01000000u #define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24 #define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00 static unsigned int sys_clock = 48000000; inline void dwt_start(void) { BSP_REG_DEM_CR |= (unsigned int)BSP_BIT_DEM_CR_TRCENA; BSP_REG_DWT_CYCCNT = (unsigned int)0u; //初始化CYCCNT寄存器 BSP_REG_DWT_CR |= (unsigned int)BSP_BIT_DWT_CR_CYCCNTENA; //開啟CYCCNT } inline void dwt_stop(void) { BSP_REG_DWT_CR = 0; } void vcom_pin_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = VCOM_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(VCOM_PORT, &GPIO_InitStructure); GPIO_SetBits(VCOM_PORT,VCOM_PIN); VCOM_PIN_HIGH; } void vcom_put_char(char ch) { int i; int dat[8]; uint32_t sys_clk, bit_width; volatile uint32_t time_stamp; sys_clk = sys_clock/1000000; bit_width = 1000000*sys_clk/VCOM_BOUND; for(i=0; i<8; i++) { if(ch & 0x01) dat[i] = 1; else dat[i] = 0; ch >>= 1; } OS_CPU_SR cpu_sr; enter_critical();//以下代碼進行臨界保護,防止被中斷打斷造成發送誤碼 dwt_start(); VCOM_PIN_LOW; //發送起始位 time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); for(i=0; i<8; i++) { if(dat[i]) VCOM_PIN_HIGH; else VCOM_PIN_LOW; time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); //發8bit 數據位 } VCOM_PIN_HIGH; time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); //發停止位 dwt_stop(); exit_critical(); } void vcom_printf(const char *fmt, ...) { char buf[0x80]; int i; va_list ap; memset(buf, 0x00, sizeof(buf)); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); i = 0; while(buf[i]) { vcom_put_char(buf[i]); i++; } }
方案2:半雙工UART
實現方式: 普通定時器+普通IO口中斷+fifo
/** *軟件串口的實現(IO模擬串口) * 波特率:9600 1-8-N * TXD : PC13 * RXD : PB14 * 使用外部中斷對RXD的下降沿進行觸發,使用定時器4按照9600波特率進行定時數據接收。 * Demo功能: 接收11個數據,然后把接收到的數據發送出去 */ #define OI_TXD PCout(13) #define OI_RXD PBin(14) #define BuadRate_9600 100 u8 len = 0; //接收計數 u8 USART_buf[11]; //接收緩沖區 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端口時鐘 //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; //下降沿觸發中斷 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); //時鐘使能 //定時器TIM4初始化 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(TIM4, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx的時間基數單位 TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,允許更新中斷 //中斷優先級NVIC設置 NVIC_InitStructure.NVIC_IRQChannel = TIM4_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寄存器 } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置中斷優先級分組為組2:2位搶占優先級,2位響應優先級 delay_init(); IOConfig(); TIM4_Int_Init(107, 71); //1M計數頻率 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)); } } }
編輯:黃飛
?
評論
查看更多