STM8s103f3p3單片機只有一個串口,有時候在實際項目中,我們需要用到多個串口來實現項目要求,此時,我們可以通過IO口來模擬串口通信,在實現項目需求的同時,還大大降低了成本。
實驗原理
默認串口空閒狀態爲高電平,開始位爲0,然後發送8個數據位,然後是奇偶校驗位,停止位爲高電平。數字電路中只有0、1兩種狀態,這是我們用IO口可以實現的,我們認爲高電平是1,低電平是0。也就是說我們用只到了IO口的輸出功能(對於實現TX功能的IO口而言),那麼到底發多長時間的高電平呢?這是由TIMx定時器決定的,TIMx定時器通過計數器實現。這個時間取決於什麼呢?取決於波特率。也就是說,只要我們初始化設置好了TIMx定時器我們就不需要考慮時間問題了。爲方便我們現發送低位,然後逐漸右移來發送高位。如何判斷髮送完一個字節呢?我們認爲10位爲一個字節。如何判斷髮送玩所有的字節呢?我們使用一個緩存區(也就是數組)緩存區的字節發送完了我們就認爲發送完了。
IO口配置
串口通信需要發送(TX)和接收(RX)兩根通信線(半雙工模式)。因此,我們需要將兩個IO端口分別配置成TX和RX。
1.IO_TX發送配置:IO基本模式配置(具體請看前幾期IO口配置博客),開啓外部中斷,觸發模式設置爲下降沿觸發;
代碼如下:
#define VM_UART_TXD_PORT_OUT PC_DDR|=1<<5 ;PC_CR1|=(1<<5 );PC_CR2 &=~(1<<5)//設定爲輸出
#define VM_UART_TXD_PORT_IN PC_DDR&=~(1<<5 );PC_CR1|=(1<<5 );PC_CR2&=~(1<<5 )//設定爲輸入
2.IO_RX發送配置:IO基本模式配置(具體請看前幾期IO口配置博客),開啓外部中斷,觸發模式設置爲上升沿和下降沿觸發
代碼如下:
#define VM_UART_RXD_PORT_IN PB_DDR&=~(1<<4);PB_CR1|=(1<<4);PB_CR2&=~(1<<4) //設置只上拉輸入 不中斷
#define VM_UART_RXD_PORT_INT_IN PB_DDR&=~(1<<4);PB_CR1|=(1<<4);PB_CR2|=(1<<4) //設置只上拉輸入 中斷
3.初始化IO口,並配置中斷及編寫中斷服務函數
代碼如下:
/**************************************************************************
全局變量
**************************************************************************/
u8 vm_UART_RX_P;//接收緩存指針
u8 vm_UART_RX_BUF[vm_UART_RX_BUF_L];//接收緩存
u8 vm_UART_RX_byte;//擴展串口 字節緩衝區
u8 vm_UART_RX_bit;//擴展串口 計算位數
u8 vm_UART_TX_byte;//擴展串口 字節緩衝區
u8 vm_UART_TX_bit;//擴展串口 計算位數
u8 vm_uart_tx_flag;//正在發送標誌
u8 vm_uart_rx_flag;//正在接收標誌
/**************************************************************************/
void IO_EXTI_init(void)
{
VM_UART_TXD_PORT_IN;//發送初始化
//外部中斷
EXTI_CR1 &=~(3<<4);//清零
EXTI_CR1 |=2<<4;//下降沿觸發
VM_UART_RXD_PORT_INT_IN;//接收初始化
//外部中斷
EXTI_CR1 &=~(3<<2);//清零
EXTI_CR1 |=3<<2;//上升沿和下降沿觸發
}
//中斷服務函數
#pragma vector=7
__interrupt void EXTI_PORTC_IRQHandler(void)
{
//外中斷一次收一個字節,只識別起始位
if((PB_IDR &0x10) == 0)
{
TIM1_START;//啓動定時器
vm_UART_RX_byte = 0;
vm_UART_RX_bit = 0;
VM_UART_RXD_PORT_IN; //只上拉輸入 不中斷
vm_uart_rx_flag = 1;//開啓發送
}
}
波特率配置
由實驗原理我們可以知道波特率可以通過配置定時器來設置,具體計算公式如下所示:
stm8波特率計算:主時鐘頻率/分頻係數/波特率=裝載值
例:波特率9600 主時鐘頻率16MHz 分頻係數1
初值=16x10^6/9600=1667
根據公式,我們可以求得初值,然後配置定時器自動裝載值,分頻係數,計數器模式,開啓中斷!開啓中斷!開啓中斷!重要事情說三遍!
發送
定時器配置(此處初始化TIM2定時器實現發送功能),中斷服務函數及串口發送函數
代碼部分:
//1.定時器2初始化
void TIM2_Configuration(u16 time, u8 en)
{
TIM2_CR1|=1<<7;////允許自動裝載值
TIM2_PSCR = 0;//預分頻係數
TIM2_ARRH = (uint8_t)(time >> 8);
TIM2_ARRL = (uint8_t)(time & 0xff); //自動裝載值
TIM2_IER = 1;//中斷使能
if(en != 0)
{
TIM2_CR1 |= 1<<0;//使能計數器
}
}
//2.定時器2中斷服務函數
#pragma vector=0xF
__interrupt void TIM2_UPD_OVF_BRK_IRQHandler(void)
{
TIM2_SR1&=~(1<<0);//清空標誌位
if(vm_UART_TX_bit < 8)//判斷數據位數
{
if((vm_UART_TX_byte & 0x1) == 0x1)
{
VM_UART_TXD_PORT_WriteHigh;
}
else
{
VM_UART_TXD_PORT_WriteLow;
}
vm_UART_TX_byte /= 2;
}
else
{
VM_UART_TXD_PORT_WriteHigh;
if(vm_UART_TX_bit > 8)//判斷數據位數
{
vm_uart_tx_flag = 0; //設置爲發送完畢
VM_UART_TXD_PORT_IN;
TIM2_STOP;//
}
}
vm_UART_TX_bit++;
}
//3.模擬串口發送函數,此處編寫了三個發送函數,根據自己需求調用
void vm_UARTsend_byte(u8 byte)
{
while(vm_uart_tx_flag == 1); //檢查是否發送完畢
vm_uart_tx_flag = 1;
VM_UART_TXD_PORT_OUT;
VM_UART_TXD_PORT_WriteLow;
vm_UART_TX_bit = 0;
vm_UART_TX_byte = byte;
TIM2_START; //計數器置零,啓動定時器
}
void vm_UART_SendString(u8 *Data, u16 len)
{
u16 i;
for(i=0; i < len; i++)
vm_UARTsend_byte(Data[i]);
}
void vm_UART_SendStr(u8 *str)
{
u16 i = 0;
while((*(str+i)) != 0)
{
vm_UARTsend_byte(*(str+i));
i++;
}
}
接收
配置定時器1(TIM1),中斷服務函數(包含接收部分)
代碼如下:
//定時器1初始化
//通過設計算定時器初值來設置波特率
/*stm8波特率計算:主時鐘頻率/分頻係數/波特率=裝載值*/
void TIM1_Configuration(u16 time, u8 en)
{
TIM1_CR1 |= 1<<7;//允許自動裝載值
/*設置自動裝載值*/
TIM1_ARRH = (uint8_t)(time >> 8);//取高八位
TIM1_ARRL = (uint8_t)(time)&0x00ff;//取低八位,高八位清零
/* 設置分頻係數*/
TIM1_PSCRH = (uint8_t)(0x0000 >> 8);
TIM1_PSCRL = (uint8_t)(0x0000)&0x00ff;
/* 選擇計數器模式 */
TIM1_CR1&= ~(1<<4);//向上計數
TIM1_CR1&=~(1<<3);//循環計數
TIM1_CR1&=~(1<<2);//更新請求源
TIM1_CR1 = 0;//清零
/* 設置重複計數模式 */
TIM1_RCR = 0x00;
/* 設置允許中斷 */
TIM1_IER = 1;//中斷使能
if(en != 0)
{
TIM1_CR1 |= 1<<0;//使能計數器
}
}
//2.定時器1中斷服務函數
#pragma vector=0xD
__interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{
TIM1_SR1&=~(1<<0);//清空標誌位
if((PB_IDR &0x10) == 0)
{
vm_UART_RX_byte /= 2;
}
else
{
vm_UART_RX_byte /= 2;
vm_UART_RX_byte |= 0X80;
}
vm_UART_RX_bit++;
if(vm_UART_RX_bit >= 8)
{
(uint8_t)PC_ODR_ODR4>0?0:1;//高取反,低取高,必須配置爲輸出
TIM1_STOP;//停止定時器
VM_UART_RXD_PORT_INT_IN;
vm_uart_rx_flag = 0;
vm_UART_RX_bit = 0;//
vm_UART_RX_BUF[vm_UART_RX_P] = vm_UART_RX_byte; //一個字節時接收完畢
vm_UART_RX_P++;
if(vm_UART_RX_P >= vm_UART_RX_BUF_L)vm_UART_RX_P = 0; //接收指針
}
}
主函數部分
1.時鐘及所有外設初始化
2.開啓全局中斷
3自行添加代碼部分
總結
本教程詳細講解了STM8單片機IO口模擬串口通線,實習那真正串口,大大降低了成本。以下是串口發送的效果圖。
寫得不好,多多指教。
工程代碼:https://download.csdn.net/download/weixin_43184208/11120674