在前兩篇文章中我們介紹了IO口模擬串口發送數據和接收數據,前兩種方法都是使用定時器來進行發送和接收,沒有用到中斷,優點是邏輯簡單,但是缺點很明顯,只能進行單個字節的發送和接收,而且不能同時工作。因此在實際工程中沒有什么作用,僅供學習使用。使用中斷方式我們可以發送和接收多個字節的數據。
1、使用中斷方式進行IO口模擬串口發送和接收數據的原理
這篇文章我將使用中斷的方式進行發送和接收,同樣的,由于原理缺陷,這篇文章介紹的方法無法同時接收和發送,而且由于發送會延時,是一個不太好的方法,僅供學習使用。
注意:這篇文章實現的IO口模擬串口無法同時接收和發送數據!如有需要在實際項目中使用IO口模擬串口工作,請移步:
1.1、發送數據的原理
我們使用定時器更新中斷來進行數據的發送,首先在發送函數中開啟中斷,然后在中斷函數中逐位發送,直到發送10位(一個字節,我們暫時沒有使用校驗位)后關閉更新中斷。
1.2、接收數據的原理
我們使用單片機的外部中斷(IO中斷)來開啟比較中斷,在比較中斷中逐位接收,直到接收了10位后關閉比較中斷,并保存接收的有效字節個數。
2、實現過程
同樣的,在實現過程中,我們在工程文件夾SimUART中共分了4個文件夾(分別為System:存放系統文件;Project:存放項目文件;User:存放main.c和UserApp.c;My_Lib:存放其它常用的文件)。根據我們將用到的單片機的資源,我們在My_Lib中分了二個文件夾,分別是——IO:存放與IO口相關函數的文件;Time:與定時器和中斷相關函數的文件。下面我貼出相關函數的.c文件,而.h文件省略不寫,有需要的同學可以根據文章后面的網址下載使用。我的編程環境是IAR,需要自己建立IAR工程。下面詳細介紹(Project和System省略不寫,其中System只用了stm8s.h)。
2.1、一切從main()函數開始
同樣的,我們建立完工程后需要從main()函數開始,為便于理解,我將使用邏輯偽代碼,邏輯偽代碼如下:
int main( void )
{
單片機時鐘初始化;
IO口初始化;
定時器初始化;
中斷初始化;
while(1)
{
if( 需要發送的數據數 > 0 )
{
發送;
需要發送的數據數 = 0;
}
}
}
我們首先需要進行初始化的配置All_Config()【在UserApp.c中】,代碼如下:
//head file
#include "UserApp.h"
#include "IO.h"
#include "User.h"
#include "Time.h"
#include "Delay.h"
u16 SimUART_SendData = 0xFF;
u8 SimUART_SendData_BitNum = 10;
u8 RxData_ValidNum = 0;
u16 RxDataValue_Temp = 0x0000;
//初始化函數
void All_Config( void )
{
Clock_Config();
IO_Init();
TIM2_Init();
EXTI_Init();
}
其中User.h是我將自己常用的宏寫在了一個文件里面,對應于main.c。在沒有接外部時鐘的時候,STM8S003F在啟動時主時鐘默認為HSI RC時鐘的8分頻,我們這里的初始化僅指定為16MHZ高速內部RC振蕩器(HSI),也可以省略不寫,Clock_Config()【在UserApp.c中】函數代碼如下:
//初始化時鐘 選擇內部16M晶振
void Clock_Config()
{
CLK->CKDIVR &= ~( BIT(4) | BIT(3) );
}
我選擇單片機的PD2作為我的模擬串口的數據發送口,選擇PD3作為我的模擬串口的數據接收口,IO_Init()【在IO.c中】函數代碼如下:
//head file
#include "IO.h"
#include "User.h"
void IO_Init()
{
//TXD:TXD位推挽輸出 PD2
SimUART_PORT->ODR |= SimUART_PIN_TX; //0000 0100
SimUART_PORT->DDR |= SimUART_PIN_TX; //0000 0100
SimUART_PORT->CR1 |= SimUART_PIN_TX; //0000 0100
SimUART_PORT->CR2 &= ~SimUART_PIN_TX; //0000 0100
//RXD:懸浮輸入 高電平 PD3
SimUART_PORT->IDR |= SimUART_PIN_RX; //0000 1000
SimUART_PORT->DDR &= ~SimUART_PIN_RX; //0000 1000
SimUART_PORT->CR1 &= ~SimUART_PIN_RX; //0000 1000
SimUART_PORT->CR2 &= ~SimUART_PIN_RX; //0000 1000
}
其中在IO.h中的宏定義為:
//宏定義
#define SimUART_PORT GPIOD
#define SimUART_PIN_TX 0X04 //PD2
#define SimUART_PIN_RX 0X08 //PD3
#define SimUART_PIN_RX_0 0X00 //PD3
#define SimUART_PIN_RX_1 0X08 //PD3
定時器的初始化和前面一樣,具體操作可以見這里。代碼如下:
void TIM2_Init()
{
CLK->PCKENR1 |= CLK_PCKENR1_TIM2; //使能 TIM2
TIM2->PSCR = 0x04; //16分頻 1MHZ 1us
TIM2->ARRH = ARRValue_9600 >> 8; //自動裝載 每52us復位一次TIM2
TIM2->ARRL = ARRValue_9600; //每1us遞減1
TIM2->CNTRH = 0; //定時器清零
TIM2->CNTRL = 0;
TIM2->CR1 |= TIM2_CR1_CEN; //開啟定時器
}
其中Time.h中的宏定義為:
#define ARRValue_9600 104
中斷初始化EXTI_Init()【在UserApp.c中】代碼如下:
//初始化中斷
void Interrupt_Init()
{
//允許更新中斷
//TIM2->CR1 &= ~TIM2_CR1_UDIS; //允許更新 可以不管默認為0
TIM2->IER |= TIM2_IER_UIE; //更新中斷使能
//IO口下降中斷 初始化
SimUART_PORT->CR2 |= 0x08; //使能外部中斷
EXTI->CR1 = 0x80; //僅下降沿觸發
//禁止比較中斷
TIM2->IER &= ~TIM2_IER_CC1IE; //禁止捕獲/比較1
}
根據上面的原理,我們知道:更新中斷是在發送函數中打開的,因此更新初始化中使能;IO中斷是通過下降沿(串口數據的起始位為低電平)打開的,因此設置成使能和下降沿觸發;比較中斷實在IO中斷中打開的,因此設置成禁止。
2.2、模擬串口發送數據
完成時鐘、IO口、定時器、中斷的初始化以后我們就可以開始主體程序的設計了,邏輯偽代碼如下:
//發送 函數
void SimUART_SendByte(u8 SendData)
{
等待一個字節發送完畢;
第一步:清除 更新更新中斷標志位(保證不進入更新中斷);
第二步:數據調整(起始位為0,數據位不變,停止位和其它位為1);
第三步: 開啟更新中斷;
}
//定時器更新中斷 發送接收到的數據
#pragma vector = 對應向量標志位
__interrupt void SimUART_Update_IRQHandler(void)
{
第一步:清除 更新中斷標志位(保證不進入更新中斷);
發送一個位計數;
if( 發送位為1 )
{
發送高電平;
}
esle
{
發送低電平;
}
移位,發送下一個位;
//完成了一個位的發送
if( 發送了10個位 )
{
關閉中斷;
}
}
進入發送函數,首先應該清除更新中斷標志位,然后寫功能代碼,結束前需要打開更新中斷,從而去執行更新中斷的代碼。我們需要考慮為何需要一個延時來等待一個字節完成發送。在中斷函數中我們是讓一個字節發送完成以后才關閉中斷的,如果不延時,可能發生一個字節還沒有發送完成,卻進入下一個更新中斷的情況,因此需要等待,我們直接用一個標志位就能解決。對應向量標志位通過查芯片手冊和頭文件可以得到。發送函數在UserApp.c中,更新中斷在Time.c中,發送部分代碼如下:
void SimUART_SendByte(u8 SendData)
{
while( SimUART_SendData_BitNum < 10 );
//清 更新中斷標志位
TIM2->SR1 &= ~TIM2_SR1_UIF;
//0000 0000 0000 0000 保證最低位(起始)為0,除數據位后全部為1
SimUART_SendData = ( ( SendData << 1 )| (0xFE00) );
SimUART_SendData_BitNum = 0;
//開啟更新中斷
TIM2-> IER |= TIM2_IER_UIE;
}
//定時器更新中斷 發送接收到的數據
#pragma vector = TIM2_Updata_vector
__interrupt void SimUART_Update_IRQHandler(void)
{
//第一步,清中斷標志位
TIM2->SR1 &= ~TIM2_SR1_UIF;
//發送一個位 計數
SimUART_SendData_BitNum++;
if( ((SimUART_SendData) & 0X0001) ) //如果是高電平,發高電平
{
SimUART_PORT->ODR |= SimUART_PIN_TX;
}
else //如果是低電平,發低電平
{
SimUART_PORT->ODR &= ~SimUART_PIN_TX;
}
//發送一個位
SimUART_SendData >>= 1;
if( 10 <= SimUART_SendData_BitNum )
{
TIM2-> IER &= ~TIM2_IER_UIE;
}
}
2.3、模擬串口接收數據
接收字節也是一個字節一個字節的接收的,由于單片機的始終可能存在誤差(這是不可避免的),因此我們需要像個方法來除去這個誤差,我們可以增大采樣速度(減少采樣時間)來排除,比如:我采集5次(發送數據波特率為9600,采集就要是這個的5倍速度),如果有三個是高電平我們就認為該位是高電平。另一種方法是,我們讓采樣點始終在一個電平的中間,這就用到了IO中斷來開啟比較中斷。具體實現的邏輯偽代碼如下:
//接收數據
//判斷一個數據的開始 IO外部中斷
#pragma vector = 對應向量標志位
__interrupt void SimUART_IO_IRQHandler(void)
{
關閉 IO中斷 ;
設置 比較中斷 ;
清除 比較中斷 中斷標志位;
打開 比較中斷 ;
}
//接收數據 比較中斷
#pragma vector = 對應向量標志位
__interrupt void SimUART_Capture_IRQHandler(void)
{
清除 比較中斷 標志位;
接收位個數計數;
if( 接收位為1 )
{
將相應的位置1;
}
if( 接收了10個位 )
{
接收位個數清零;
關 比較中斷;
清除 IO中斷標志位;
開 IO中斷;
接收到的有效字節個數計數;
}
}
兩個中斷函數均在Time.c中,具體實現代碼如下:
//判斷一個數據的開始 IO外部中斷
#pragma vector = EXTI3_PD_vector
__interrupt void SimUART_IO_IRQHandler(void)
{
//關閉IO中斷
SimUART_PORT->CR2 &= ~0x08;
//設置比較中斷
TIM2-> CCMR1 &= 0x00; //還是有問題
//TIM2-> CCR1H = 0;
TIM2-> CCR1L = TIM2->CNTRL + ( ARRValue_9600/2 );
RxDataValue = 0x0000;
//TIM2->CCER1 |= 0x00;
TIM2->SR1 &= ~TIM2_SR1_CC1IF; //清中斷標志位
TIM2->IER |= TIM2_IER_CC1IE; //使能 捕獲/比較中斷1
}
//接收數據 比較中斷
#pragma vector = TIM2_Capture_vector
__interrupt void SimUART_Capture_IRQHandler(void)
{
//第一步,清中斷標志位(防止始終進入中斷)
TIM2->SR1 &= ~TIM2_SR1_CC1IF; //清中斷標志位
RxDataNum++;
if( SimUART_PIN_RX_1 == (SimUART_PORT->IDR & SimUART_PIN_RX_1 ) )//其次,接收10個位
{
RxDataValue |= ( 0x01 << (RxDataNum) );//該位 置1
}
//第二步,讀IO輸入
//首先,判斷是否接收了10個位
if(10 == RxDataNum) //如果是則
{
RxDataNum = 0;
TIM2->IER &= ~TIM2_IER_CC1IE; //1、關 比較中斷
//STM8S沒有外中斷標志位,STM8L有標志位,因此暫時不需要清中斷標志位
SimUART_PORT->CR2 |= 0x08; //2、開 IO中斷
//處理有效數據
if( (RxDataValue & 0x0402) == 0x0400 )
{
RxDataValue_Temp = ( RxDataValue >> 2 );
RxData_ValidNum++;//接收字有效 節數
}
}
}
2.4、補全main()函數
int main( void )
{
All_Config(); //初始化
_asm("rim"); //開總中斷
while(1) //發送循環
{
if( RxData_ValidNum > 0x00 )
{
SimUART_SendByte( RxDataValue_Temp );
RxData_ValidNum = 0x00;
}
}
//return 0;
}
3、結束語
至此我們使用中斷的方法來進行IO口模擬串口(未使用庫函數)收發數據的功能已經實現,在本文章中,為了方便,我使用我的發送數據來驗證我接收數據的正確性,因此先寫的發送數據,再寫的接收數據。正如前面所說,我是一個位一個位的發送和接受,在發送過程中有發送延時,這樣的后果是如果是一個字符串的收發是沒問題的,但是由于沒有使用緩存區(即一個數組),導致我們收發數據不能分布于各個任務中,代碼在實際項目中可能會出現一些問題,例如已接受就得發送,否則會出現錯誤,這回影響單片機在執行任務時產生問題。我將在后面進行介紹我們在實際工程中能夠使用的全雙工串口程序。值得注意的是,收發應該是單獨存在的,我這里是為了方便反而讓我的發送程序發送接收到的數據。
上一篇:STM8 IO口模擬串口通信
下一篇:【嵌入式開發】STM8S103F3P6單線半雙工串口通信
推薦閱讀
史海拾趣
Amphion Semiconductor Ltd 成立于 1995 年,是一家總部位于英國劍橋的半導體設計公司,專注于數字信號處理器(DSP)和嵌入式系統解決方案。以下是關于 Amphion Semiconductor Ltd 公司發展的五個相關故事:
創立與早期階段:Amphion Semiconductor Ltd 公司由 David Belbin 和 Richard Smith 共同創立于 1995 年,起初專注于開發用于音頻和視頻處理的數字信號處理器。公司總部設立在英國劍橋,這個地區是全球半導體設計和科技創新的重要中心之一。
技術創新與產品發展:Amphion Semiconductor Ltd 公司在數字信號處理領域進行了持續的技術創新和產品開發。公司的DSP技術在音頻和視頻處理、通信、圖像處理等領域得到了廣泛應用。除了開發自有的DSP芯片,公司還提供定制化的嵌入式系統解決方案,滿足客戶特定的應用需求。
業務擴展與市場拓展:隨著技術的成熟和市場需求的增長,Amphion Semiconductor Ltd 公司逐漸擴大了業務規模,并在國際市場上取得了一定的份額。公司與全球各種行業的客戶建立了合作關系,包括消費電子、汽車、通信、工業控制等領域,拓展了市場覆蓋范圍。
收購與合并:Amphion Semiconductor Ltd 公司在發展過程中進行了一些收購和合并,以擴大業務范圍和提升競爭力。其中,2013年公司被丹麥半導體公司 Nordic Semiconductor 收購,成為其子公司,為 Nordic Semiconductor 擴展了音頻和視頻處理領域的技術能力。
變革與發展方向:Amphion Semiconductor Ltd 公司隨著技術和市場的發展不斷調整發展方向,加大在新興領域的投入和研發力度。公司積極探索人工智能、物聯網、自動駕駛等領域的應用,致力于提供更加智能化和高效的嵌入式系統解決方案,以適應不斷變化的市場需求。
這些故事展示了 Amphion Semiconductor Ltd 公司從創立初期到如今在技術創新、產品發展、業務拓展、收購與合并以及發展方向等方面取得的重要進展。
進入21世紀,電子行業的競爭日益激烈,鈺創科技意識到必須不斷進行技術創新才能保持競爭優勢。公司加大了研發投入,成功開發出了一系列先進的內存芯片和系統芯片技術。這些技術突破不僅提升了產品的性能和質量,也幫助公司贏得了更多國際客戶的認可和信任。
深圳市飛翼科技有限公司自2006年成立以來,一直致力于模擬與數字MCU混合芯片領域的研究、設計和開發應用。公司主攻電容式觸摸感應按鍵芯片設計,憑借多項獨有的專利技術,成功突破了行業內的技術難點。經過多年的努力,飛翼科技已成為該應用領域中技術最全面、市場份額最大的公司之一。其電容式觸摸感應芯片廣泛應用于各類電子產品中,為用戶帶來了更加便捷、智能的交互體驗。
品質是Akros公司的生命線。公司始終堅持嚴格的質量控制體系,從原材料采購到產品出廠的每一個環節都進行嚴格把關。為了確保產品的品質穩定可靠,Akros還引進了先進的生產設備和檢測儀器。同時,公司還注重持續改進,通過不斷優化生產流程和提升員工技能水平,不斷提高產品的品質和效率。
在追求經濟效益的同時,Box Enclosures公司也積極履行社會責任。公司注重環保和可持續發展,采用環保材料和節能技術生產產品。同時,公司還積極參與公益事業,為社會做出貢獻。這種對社會責任的承擔和綠色發展的理念,使得Box Enclosures公司贏得了社會的認可和尊重。
以上是關于Box Enclosures公司發展的5個虛構故事,雖然這些故事是基于虛構的,但它們可能反映了Box Enclosures公司在實際發展中可能遇到的一些情況和挑戰。
如何優化單片機C語言代碼(程序員必讀) 1、選擇合適的算法和數據結構 ig 5Ce;P8R 應該熟悉算法語言,知道各種算法的優缺點,具體資料請參見相應的參考資料,有 Y YA/QLJ6 很多計算機書籍上都有介紹。將比較慢的順序查找法用較快的二分查找 ...… 查看全部問答∨ |
怎樣做才能快速的嵌入式了? 其實,做工程是沒有捷徑可走的.聽聽下面一個大牛寫的! 先說做硬件: 把你的數字電路教材和模擬電路的教材讀熟,暫時先把重點放在數字電路上面,接著把微機原理和接口技術讀熟悉了,最好能用匯編寫幾個簡單的程序上機 ...… 查看全部問答∨ |
一、基本概念 1.ARM cortex_m3內核支持256個中斷(16個內核+240外部)和可編程256級中斷優先級的設置,與其相關的中斷控制和中斷優先級控制寄存器(NVIC、SYSTICK等)也都屬于cortex_m3內核的部分。STM32采用了cortex_m3內核,所以這部分仍舊保留 ...… 查看全部問答∨ |
|
請教各位高手,注冊表[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\PCI\\VEN_1022&DEV_2000&SUBSYS_20001022&REV_10\\3&61aaa01&0&88]中3&61aaa01&0&88代表什么意思?謝謝!… 查看全部問答∨ |
|
基于WINCE6.0 R3定制的模擬器NK能玩QQ但上不了網,為什么?Ping主機也是可以通的. 基于WINCE6.0 R3定制的模擬器NK能玩QQ但上不了網,為什么?Ping主機也是可以通的.… 查看全部問答∨ |
以前是用STM32F103V8的,這次我換成了STM32F103VB,買了30,可是出現問題了,共焊接了兩塊板,一塊板的晶振只會在偶爾上電時會起振(11.0592M),而另一塊板子可以起振,但程序跑起來不對,而我又把以前板子的STM32F103V8換到這塊板子上來又是好 ...… 查看全部問答∨ |