編寫本文稿的目的,在於經過分析stm32平臺上的串口中斷源碼,學習 php
RTT中如何編寫中斷處理程序html
如何編寫RTT設備驅動接口代碼node
瞭解串行設備的常見處理機制編程
先以RTT官方源碼中的STM32 BSP包來分析。rt-thread\bsp\stm32f10x 下,涉及的文件爲: 數組
usart.c 網絡
usart.h數據結構
serail.c框架
serail.h函數
編寫uart的驅動程序,首先須要瞭解RTT的設備框架,RTT的設備框架咱們已經大體的介紹了一下,這裏以usart的驅動來具體分析RTT的IO設備管理。注:參考《RTT實時操做系統編程指南》 I/O設備管理一章。 源碼分析
咱們能夠將USART的硬件驅動分紅兩個部分,以下圖所示
+----------------------+
| rtt下的usart設備驅動 |
|---------------------- |
| usart硬件初始化代碼 |
|---------------------- |
| usart 硬件 |
+----------------------+
實際上,在缺少操做系統的平臺,即裸機平臺上,咱們一般只須要編寫USART硬件初始化代碼便可。而引入了RTOS,如RTT後,RTT中自帶IO設備管理層,它是爲了將各類各樣的硬件設備封裝成具備統一的接口的邏輯設備,以方便管理及使用。
讓咱們從下向上看,先來看看USART硬件初始化程序,這部分代碼位於usart.c和usart.h中。
假如在接觸RTT以前,你已經對stm32很熟悉了,那麼此文件中定義的函數名必定讓你倍感親切。這裏實現的函數有:
static void RCC_Configuration(void);
static void GPIO_Configuration(void);
static void NVIC_Configuration(void);
static void DMA_Configuration(void);
void rt_hw_usart_init();
前四個函數,是跟ST官方固件庫提供的示例代碼的名字保持一致。這些函數內部也是直接調用官方庫代碼實現的。具體再也不贅述。
對STM32裸機開發尚不太熟悉的朋友,建議先去官方網站下載官方固件源碼包,以及應用手冊和示例程序,ST提供了大量的文檔和示例代碼,利用好這些資源能夠極大地加快開發。
如今來重點關注一下最後一個函數,即 rt_hw_usart_init函數的實現。
/* * Init all related hardware in here * rt_hw_serial_init() will register all supported USART device */ void rt_hw_usart_init() { USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; RCC_Configuration(); GPIO_Configuration(); NVIC_Configuration(); DMA_Configuration(); /* uart init */ #ifdef RT_USING_UART1 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; USART_Init(USART1, &USART_InitStructure); USART_ClockInit(USART1, &USART_ClockInitStructure); /* register uart1 */ rt_hw_serial_register(&uart1_device, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM, &uart1); /* enable interrupt */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); #endif #ifdef RT_USING_UART2 .... #endif #ifdef RT_USING_UART3 .... #endif }
上述代碼中,大部分代碼都是調用ST庫函數,請注意下列語句。
/* register uart1 */ rt_hw_serial_register(&uart1_device, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM, &uart1);
這個函數的實現位於serial.c中,咱們將在下一小節分析,暫且不表。
顯然,函數rt_hw_usart_init,顧名思義,是用於初始化USART硬件的函數,所以這個函數必定會在USART使用以前被調用。搜索工程發現,這個函數是在board.c中rt_hw_board_init函數中被調用,而rt_hw_board_init函數又是在startup.c裏的 rtthread_startup函數中調用的。進一步在startup.c的main函數中調用的,咱們將實際的路徑調用過程繪製以下。
startup.c main() ---> startup.c rtthread_startup() ---> board.c rt_hw_board_init() ---> usart.c rt_hw_usart_init()
到這裏,USART硬件的初始化工做已經完成完成了99%,下一步,咱們須要爲USART編寫代碼,將其歸入到RTT的設備管理層之中,正如前面所說,這部分代碼在serial.c中實現。咱們來重點分析這一文件。
要想將某個設備歸入到RTT的IO設備層中,須要爲這個設備建立一個名爲rt_device的數據結構。該數據結構在rtdef.h中定義。
/** * Device structure */ struct rt_device { struct rt_object parent; /**< inherit from rt_object */ enum rt_device_class_type type; /**< device type */ rt_uint16_t flag, open_flag; /**< device flag and device open flag */ rt_uint8_t device_id; /* 0 - 255 */ /* device call back */ rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size); rt_err_t (*tx_complete)(rt_device_t dev, void* buffer); /* common device interface */ rt_err_t (*init) (rt_device_t dev); rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); rt_err_t (*close) (rt_device_t dev); rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); rt_err_t (*control)(rt_device_t dev, rt_uint8_t cmd, void *args); #ifdef RT_USING_DEVICE_SUSPEND rt_err_t (*suspend) (rt_device_t dev); rt_err_t (*resumed) (rt_device_t dev); #endif void *user_data; /**< device private data */ };
對這個數據結構作一些詳細的說明。
struct rt_object parent;這個域是RTT的所謂的面向對象設計,跟咱們關係不大。
type域配合前面的parent域,來制定設備的類型,也與咱們關係不大。
flag和openflag用來存儲設備的權限,好比是隻讀,仍是讀寫等等。
device_id即設備號,每個設備都擁有惟一的編號,內核能夠根據這個編號查找到設備。
接下來就是定義了一組函數指針,用於操做這個設備的一些回調(callback)函數。他們分別是:
rx_indicate tx_complete init open close read write control
以及一個指針變量,由用戶根據實際須要填充
void *user_data;
若是在rtconfig.h中使能了RT_USING_DEVICE_SUSPEND宏,還會增長兩個函數
rt_err_t (*suspend) (rt_device_t dev); rt_err_t (*resumed) (rt_device_t dev);
這些域並不必定所有填充,後面咱們會看到對於有些函數,能夠爲其填充一個空函數。
RTT的設備管理,能夠簡單的歸納爲:每個設備都會用於一個rt_device數據結構,這些數據結構經過某種方式組織起來,每一個數據結構都會有一個惟一的device_id,以及一組硬件操做函數等等。這樣硬件就被抽象成統一的邏輯設備了,即rt_device。
還有一個小問題,device_id是純粹的數字,因此難以記憶,所以RTT中爲其分配一個ascii碼字符串來以方即是使用,好比將字符串」uart」和usart的rt_device數據結構關聯起來,這和網絡裏,ip地址很差記憶,所以使用域名系統是一個道理。
那麼天然而然,咱們須要一些函數來操做邏輯設備,這些函數在rt-thread/src/device.c文件中提供,它們是:
rt_err_t rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags)
將rt_device數據結構加入到RTT的設備層中,這個過程稱爲「註冊」。RTT的設備管理層會爲這個數據結構建立惟一的device_id。
rt_err_t rt_device_unregister(rt_device_t dev)
與註冊相反,天然是註銷了,將某個設備從RTT的設備驅動層中移除。
rt_device_t rt_device_find(const char *name)
根據設備的字符串名查找某個設備。
rt_err_t rt_device_init(rt_device_t dev)
經過調用rt_device數據結構中的init函數來初始設備。
rt_err_t rt_device_init_all(void)
初始化RTT設備管理層中的全部已註冊的設備
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
經過調用rt_device數據結構中的open函數來打開設備。
rt_err_t rt_device_close(rt_device_t dev)
經過調用rt_device數據結構中的close函數來關閉設備。
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
經過調用rt_device數據結構中的read函數來從設備上讀取數據。
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
經過調用rt_device數據結構中的write函數來向設備寫入數據(好比設備是flash,SD卡等,nand or nor flash等等)。
說明:關於這些函數各個參數的做用,建議參考官方提供的API文檔。http://www.rt-thread.org/rt-thread/rttdoc_1_0_0/group___device.html
相對於stm32的內核來講,USART是一種低速的串行設備,而且爲了最大的發揮的MCU的性能,所以使用中斷方式實現接收(發送也可使用DMA方式)。這些已經在usart.c中使能了。
先來考慮串口接受數據的狀況,串口收到一個字節的數據,就會觸發串口中斷USART1_IRQHandler,數據字節會存放於串口的硬件寄存器中。可是在RTOS中,一般存在多個線程,若是某個處理串口數據的線程在沒有串口數據時阻塞,當下一串口數據到來時,若是該數據線程依然沒有喚醒並啓動,並讀取串口字節,則上一個串口字節丟失了,所以這不是一個優良的設計,咱們須要設計一種機制來解決這種潛在的問題。實際上,緩衝機制能夠大大緩解這個問題。
所謂緩衝機制,簡略的來講,即開闢一個緩衝區,能夠是靜態數組,也能夠是malloc(或mempool)申請的動態緩衝區。在串口中斷中,先從串口的硬件寄存器中讀取數據,並保存到緩衝區中。這種狀況下,咱們須要兩個變量,一個用於標記當前寫入的位置,另一個用來表示已經被處理的數據的位置。這樣當數據處理線程阻塞時,連續收到的數據會保存到緩衝區中而避免了丟失。當中斷中已經接收到了一些串口數據後,數據處理線程終於就緒,並開始處理數據,一般來講處理數據的速度必然比接受到的數據要快,所以這樣就能解決前面所說的問題。
【圖】
聰明的讀者發現了,還有一個小問題,緩衝區的長度必然是有限的,終歸會有用到頭的時候,那該怎麼辦呢?別擔憂,緩衝區前面已經被處理過的數據所佔用的空間按天然能夠重複使用,即,當接收指針指向了緩衝區末尾時,只要緩衝區頭的數據已經被處理過了,天然能夠直接將緩衝區指針重新設置爲頭,對於表示已處理的指針變量同理。這樣這個緩衝區也就成爲了一個環形緩衝區。
關於環形緩衝區,能夠參考:http://www.rt-thread.org/dokuwiki/doku.php?id=rt-thread%E4%B8%80%E8%88%AC%E6%80%A7%E9%97%AE%E9%A2%98
RTT在stm32的串口發送上,爲了最大限度的發揮硬件的效能,使用了DMA來實現自動發送。同接收相似,也使用了緩衝機制。不過由於涉及的DMA,這個機制實現稍微複雜,咱們將在稍後作分析。
先來看看一些重要數據結構,它們在serial.h中定義:
/* STM32F10x library definitions */ #include <stm32f10x.h> #define UART_RX_BUFFER_SIZE 64 #define UART_TX_DMA_NODE_SIZE 4 /* data node for Tx Mode */ struct stm32_serial_data_node { rt_uint8_t *data_ptr; rt_size_t data_size; struct stm32_serial_data_node *next, *prev; }; struct stm32_serial_dma_tx { /* DMA Channel */ DMA_Channel_TypeDef* dma_channel; /* data list head and tail */ struct stm32_serial_data_node *list_head, *list_tail; /* data node memory pool */ struct rt_mempool data_node_mp; rt_uint8_t data_node_mem_pool[UART_TX_DMA_NODE_SIZE * (sizeof(struct stm32_serial_data_node) + sizeof(void*))]; }; struct stm32_serial_int_rx { rt_uint8_t rx_buffer[UART_RX_BUFFER_SIZE]; rt_uint32_t read_index, save_index; }; struct stm32_serial_device { USART_TypeDef* uart_device; /* rx structure */ struct stm32_serial_int_rx* int_rx; /* tx structure */ struct stm32_serial_dma_tx* dma_tx; };
能夠看到,對於stm32的串行設備,抽象爲一個專門的數據結構 struct stm32_serial_device uart_device域將用來填充具體的硬件USART指針,在stm32系列芯片上,可能存在USART1到USART3多個硬件USART。
int_rx是一個專門的用於處理接受數據的數據結構指針。dma_tx同理,關於它們的具體定義都在前面的代碼中。
struct stm32_serial_int_rx {
rt_uint8_t rx_buffer[UART_RX_BUFFER_SIZE]; rt_uint32_t read_index, save_index;
}; 能夠看到,跟上一小節說明相似,這裏定義了一個名爲rx_buffer的緩衝區,而且定義了兩個變量read_index表示已經讀取(即已被處理)的索引,而save_index,則表示下一個能夠用於存儲接受數據的索引。
接下來,讓咱們深刻代碼,來看看到底是如何處理的:首先來看看USART1_IRQHandler(void)的源碼,位於stm32f10x_it.c中
void USART1_IRQHandler(void) { #ifdef RT_USING_UART1 extern struct rt_device uart1_device; extern void rt_hw_serial_isr(struct rt_device *device); /* enter interrupt */ rt_interrupt_enter(); rt_hw_serial_isr(&uart1_device); /* leave interrupt */ rt_interrupt_leave(); #endif }
在RTT下的每個中斷服務子程序的入口都調用了
rt_interrupt_enter();
在中斷函數的子程序的出口則調用了
rt_interrupt_leave();
中間的函數 rt_hw_serial_isr,來重點關注一下:
/* ISR for serial interrupt */ void rt_hw_serial_isr(rt_device_t device) { struct stm32_serial_device* uart = (struct stm32_serial_device*) device->user_data; if(USART_GetITStatus(uart->uart_device, USART_IT_RXNE) != RESET) //判斷標誌位,判斷是不是能了接受中斷 { /* interrupt mode receive */ RT_ASSERT(device->flag & RT_DEVICE_FLAG_INT_RX); /* save on rx buffer */ while (uart->uart_device->SR & USART_FLAG_RXNE) //從datasheet上查到,SR的RXNE標誌位表示確實接收到了字節 { rt_base_t level; /* disable interrupt */ //暫時關閉中斷,由於要操做uart數據結構 level = rt_hw_interrupt_disable(); /* save character */ uart->int_rx->rx_buffer[uart->int_rx->save_index] = uart->uart_device->DR & 0xff; uart->int_rx->save_index ++; //下面的代碼檢查save_index是否已經到到緩衝區尾部,若是是則迴轉到頭部,稱爲一個環形緩衝區 if (uart->int_rx->save_index >= UART_RX_BUFFER_SIZE) uart->int_rx->save_index = 0; //這種狀況表示反轉後的save_index追上了read_index,則增大read_index,丟棄一箇舊的數據 /* if the next position is read index, discard this 'read char' */ if (uart->int_rx->save_index == uart->int_rx->read_index) { uart->int_rx->read_index ++; if (uart->int_rx->read_index >= UART_RX_BUFFER_SIZE) uart->int_rx->read_index = 0; } /* enable interrupt */ //uart數據結構已經操做完成,從新使能中斷 rt_hw_interrupt_enable(level); } /* clear interrupt */ USART_ClearITPendingBit(uart->uart_device, USART_IT_RXNE); /* invoke callback */ if (device->rx_indicate != RT_NULL) { rt_size_t rx_length; /* get rx length */ rx_length = uart->int_rx->read_index > uart->int_rx->save_index ? UART_RX_BUFFER_SIZE - uart->int_rx->read_index + uart->int_rx->save_index : uart->int_rx->save_index - uart->int_rx->read_index; device->rx_indicate(device, rx_length); } } if (USART_GetITStatus(uart->uart_device, USART_IT_TC) != RESET) { /* clear interrupt */ USART_ClearITPendingBit(uart->uart_device, USART_IT_TC); } }
這裏來重點說明一下下面代碼的做用。【繪製圖形,待添加】
//這種狀況表示反轉後的save_index追上了read_index,則增大read_index,丟棄一箇舊的數據 /* if the next position is read index, discard this 'read char' */ if (uart->int_rx->save_index == uart->int_rx->read_index) { uart->int_rx->read_index ++; if (uart->int_rx->read_index >= UART_RX_BUFFER_SIZE) uart->int_rx->read_index = 0; }
這段代碼又是作什麼用的呢?
/* invoke callback */ if (device->rx_indicate != RT_NULL) { rt_size_t rx_length; /* get rx length */ rx_length = uart->int_rx->read_index > uart->int_rx->save_index ? UART_RX_BUFFER_SIZE - uart->int_rx->read_index + uart->int_rx->save_index : uart->int_rx->save_index - uart->int_rx->read_index; device->rx_indicate(device, rx_length); }
默認狀況下usart的rt_device結構體中rx_indicate域被置空,所以不會運行這一段代碼。若是使用rt_device_set_rx_indicate(rt_device_t dev, rt_err_t(* rx_ind)(rt_device_t dev, rt_size_t size))函數爲一個串口設備註冊了接收事件回調函數,在該串口接收到數據後,就會調用以前註冊的rx_ind函數,將當前設備指針以及待讀取的數據長度做爲調用參數傳遞給用戶
分析完畢中斷處理程序,接下來咱們要分析rt_devcie數據結構中,open,read等函數的編寫。
init函數完成對設備數據結構的初始化工做。 RTT的設備驅動存在大量的預約義宏,它們在rtdef.h中定義。
(1)接收/發送模式,彷佛共有三種,分別是中斷模式,DMA模式和輪詢模式。在serial.c中,對於接收,只支持中斷模式,和輪詢模式。對於發送,只支持輪詢發送模式和DMA發送模式。
|------+----------------+----------------+---------| | | FLAG_INT_RX/TX | FLAG_DMA_RX/TX | polling | |------+----------------+----------------+---------| | 發送 | Yes | no | yes | |------+----------------+----------------+---------| | 接受 | no | yes | yes | |------+----------------+----------------+---------|
(2)設備權限分爲只讀,只寫和讀寫三種,分別由 RT_DEVICE_FLAG_RDONLY 只讀 RT_DEVICE_FLAG_WRONLY 只寫 RT_DEVICE_FLAG_RDWR 讀寫
(3)設備當前狀態 RT_DEVICE_FLAG_REMOVABLE 可移除設備 RT_DEVICE_FLAG_STANDALONE 啥意思??? RT_DEVICE_FLAG_ACTIVATED 設備處於活動狀態,表示設備已經被init過了 RT_DEVICE_FLAG_SUSPENDED 設備當前被掛起 RT_DEVICE_FLAG_STREAM 設備處於流模式(到底啥意思?–字符串模式,發送數據時會在'\n'前自動添加一個'\r')
注意,上面描述的這麼多狀態,在一個設備驅動中並不是所有都須要予以支持。這須要根據自驅動的實際狀況實現。
先來看init函數,以下所示:
static rt_err_t rt_serial_init (rt_device_t dev) { struct stm32_serial_device* uart = (struct stm32_serial_device*) dev->user_data; if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) { if (dev->flag & RT_DEVICE_FLAG_INT_RX) { rt_memset(uart->int_rx->rx_buffer, 0, sizeof(uart->int_rx->rx_buffer)); uart->int_rx->read_index = 0; uart->int_rx->save_index = 0; } ...... /* Enable USART */ USART_Cmd(uart->uart_device, ENABLE); dev->flag |= RT_DEVICE_FLAG_ACTIVATED; } return RT_EOK; } 開始時,設備的dev->flag域全是0,即爲非激活模式,若是RX爲INT_RX,模式,能夠看到即簡單的將uart->int_rx所有清0。
由於在usart.c中已經初始usart設備,而後init中經過USART_Cmd語句後,串口就會開始工做。所以open函數設置爲空便可
同colse,之間置空便可
static rt_size_t rt_serial_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
pos表示讀寫的位置,buffer是用於存儲讀取到數據的緩衝區。size爲字節數目。對於USART這種串行的流設備來講,pos沒有意義,所以這裏的pos沒有意義。 rt_device數據結構dev的的 user_data域存放了(struct stm32_serial_device*)型指針。【待修改】若是採用INT_RX模式,即中斷接受模式,則主體代碼爲
while (size) { rt_base_t level; /* disable interrupt */ level = rt_hw_interrupt_disable(); if (uart->int_rx->read_index != uart->int_rx->save_index) { /* read a character */ *ptr++ = uart->int_rx->rx_buffer[uart->int_rx->read_index]; size--; /* move to next position */ uart->int_rx->read_index ++; if (uart->int_rx->read_index >= UART_RX_BUFFER_SIZE) uart->int_rx->read_index = 0; } else { /* set error code */ err_code = -RT_EEMPTY; /* enable interrupt */ rt_hw_interrupt_enable(level); break; } /* enable interrupt */ rt_hw_interrupt_enable(level); } </code c> 上述代碼很容易理解,再也不贅述。 若是採用查詢模式,則主體代碼爲: <code c> /* polling mode */ while ((rt_uint32_t)ptr - (rt_uint32_t)buffer < size) { while (uart->uart_device->SR & USART_FLAG_RXNE) { *ptr = uart->uart_device->DR & 0xff; ptr ++; } }
這個函數返回實際讀到的數據數目。
向串口寫入數據,即發送數據。
(1) INT_TX模式,則報錯
/* interrupt mode Tx, does not support */ RT_ASSERT(0);
(2) DMA模式
寫操做處理部分:
if (dev->flag & RT_DEVICE_FLAG_DMA_TX) { /* DMA mode Tx */ /* allocate a data node */ struct stm32_serial_data_node* data_node = (struct stm32_serial_data_node*) rt_mp_alloc (&(uart->dma_tx->data_node_mp), RT_WAITING_FOREVER); if (data_node == RT_NULL) { /* set error code */ err_code = -RT_ENOMEM; } else { rt_uint32_t level; /* fill data node */ data_node->data_ptr = ptr; data_node->data_size = size; /* insert to data link */ data_node->next = RT_NULL; /* disable interrupt */ level = rt_hw_interrupt_disable(); data_node->prev = uart->dma_tx->list_tail; if (uart->dma_tx->list_tail != RT_NULL) uart->dma_tx->list_tail->next = data_node; uart->dma_tx->list_tail = data_node; if (uart->dma_tx->list_head == RT_NULL) { /* start DMA to transmit data */ uart->dma_tx->list_head = data_node; /* Enable DMA Channel */ rt_serial_enable_dma(uart->dma_tx->dma_channel, (rt_uint32_t)uart->dma_tx->list_head->data_ptr, uart->dma_tx->list_head->data_size); } /* enable interrupt */ rt_hw_interrupt_enable(level); } }
在DMA發送模式下,uart驅動將爲每次寫操做分配一個data_node數據節點,將本次寫入的數據指針地址、長度寫入此節點,並其插入到uart→dma_tx鏈表尾部,等待DMA中斷處理此節點。
若判斷到當前發送鏈表頭爲空時
uart->dma_tx->list_head == RT_NULL
說明沒有正在進行的DMA活動,則將新加入的節點設置爲鏈表頭,啓動DMA,開始發送數據。
(3)輪詢模式
/* polling mode */ if (dev->flag & RT_DEVICE_FLAG_STREAM) { /* stream mode */ while (size) { if (*ptr == '\n') { while (!(uart->uart_device->SR & USART_FLAG_TXE)); uart->uart_device->DR = '\r'; /* interrupt mode Tx, does not support */ RT_ASSERT(0); } while (!(uart->uart_device->SR & USART_FLAG_TXE)); uart->uart_device->DR = (*ptr & 0x1FF); ++ptr; --size; } } else { /* write data directly */ while (size) { while (!(uart->uart_device->SR & USART_FLAG_TXE)); uart->uart_device->DR = (*ptr & 0x1FF); ++ptr; --size; } }
從上面的代碼能夠看到,所謂的STREAM模式,即在字符串中遇到\n換行,則自動插入\r回車符。
static rt_err_t rt_serial_control (rt_device_t dev, rt_uint8_t cmd, void *args) { struct stm32_serial_device* uart; RT_ASSERT(dev != RT_NULL); uart = (struct stm32_serial_device*)dev->user_data; switch (cmd) { case RT_DEVICE_CTRL_SUSPEND: /* suspend device */ dev->flag |= RT_DEVICE_FLAG_SUSPENDED; USART_Cmd(uart->uart_device, DISABLE); break; case RT_DEVICE_CTRL_RESUME: /* resume device */ dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; USART_Cmd(uart->uart_device, ENABLE); break; } return RT_EOK; }
這個函數很是容易懂,再也不贅述。
rt_err_t rt_hw_serial_register(rt_device_t device, const char* name, rt_uint32_t flag, struct stm32_serial_device *serial) { RT_ASSERT(device != RT_NULL); if ((flag & RT_DEVICE_FLAG_DMA_RX) || (flag & RT_DEVICE_FLAG_INT_TX)) { RT_ASSERT(0); } device->type = RT_Device_Class_Char; device->rx_indicate = RT_NULL; device->tx_complete = RT_NULL; device->init = rt_serial_init; device->open = rt_serial_open; device->close = rt_serial_close; device->read = rt_serial_read; device->write = rt_serial_write; device->control = rt_serial_control; device->user_data = serial; /* register a character device */ return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag); }
上面的函數也一樣利於理解,只是簡單的填充device數據結構。須要注意兩個地方。
device->user_data = serial;
user_data域用於存儲struct stm32_serial_device *serial
最後調用rt_device_register函數將rt_device註冊到RTT的設備層中,全部的設備將造成一個鏈表。