通常我們認為以計算機CPU為核心,其外部的所有的設備都可以稱為是外部輸入輸出設備。例如計算機中的顯示器就是一個輸出設備,它的作用是將一些數字信號轉化為圖形信號顯示在電子屏幕上,其數據是由內向外流動,因此我們稱顯示器為輸出設備。
又如鍵盤和鼠標是輸入設備,它們的作用是將使用者的命令信號轉化為數字信號傳遞給計算機的CPU,其數據是由外向內流動,因此我們稱鍵盤和鼠標為輸入設備。又如,打印機是一個輸出設備,游戲手柄和攝像頭是輸入設備。如下圖:
通常的,我們將鍵盤稱為計算機的標準輸入設備,將顯示器稱為計算機的標準輸出設備。這也是Unix和類Unix系統中一直延用的名稱。我們在做虛擬文件系統時將每一個進程中都分配了一個文件描述結構體數組:
//進程控制塊Process Control Block
typedef struct pcb_s
{
//進程棧頂地址
void *p_stack;
//棧內存地址,釋放、統計內存時使用
void *p_stack_mem;
//棧內在大小
uint32_t stack_size;
//優先級由高0到低32
uint8_t prio;
//任務狀態
uint8_t status;
//任務休眠ticks
uint32_t sleep_tick;
//任務入口函數
void (*task_entry)(void *);
//任務函數參數
void *task_arg;
//進程的文件描位圖,1表示空閑,0表示使用
uint32_t f_use_map;
//進程的文件描述結構體數組
vfs_node_s *fnodes[FNODE_SIZE];
} pcb_s;
這個fnodes[FNODE_SIZE]文件描述數組記錄了進程所打開每一個設備文件的地址。這個數組的下標就是我們使用open()函數所返回的值,也就是我們通常所說的文件描述符。文件描述符為int類型,范圍通常是0~FNODE_SIZE。當文件描述符小于0時表示打開設備文件失敗。幾乎所有的類Unix系統中都使用了標準輸入、標準輸出和標準錯誤這3個IO設備。
所以,操作系統為每一個進程分配文件描述符時,會默認將0、1、2分別用于表示標準輸入、標準輸出和標準錯誤這3個設備。所以通常情況下我們使用open()函數來打開一個設備文件時, 返回的文件描述大多數是以3開始的,在同一個進程中同時打開多個設備文件,其文件描述符通常會是3、4、5、6、7、8……等。
實際上,我們在嵌入式領域里的處理器性能有限,外設的各類和功能多種多樣,并非像個人電腦或是網絡服務器一樣通用和統一。在單片機領域中我們所使用的操作系統并非一定要遵循Unix標準或習慣,但為了學習其優秀的設計理念,我們可以將我們的嵌入式操作系統中實現標準輸入、標準輸出和標準錯誤這3個設備。
在Cortex-M3處理器中我們可以使用串口設備作為操作系統中的標準輸入和標準輸出,而從本質上講標準錯誤這個設備的功能跟標準輸出是一樣的,只不過其顯示的內容都是程序錯誤,我們不單獨來實現標準錯誤設備,而只來完成標準輸入和標準輸出這兩個設備。例如串口1的初始化、讀、寫程序如下:
void serial1_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = SERIAL_BAUTRATE;
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;
USART1- >CR1 |= (USART_CR1_RE | USART_CR1_TE);
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
void serial1_write(uint8_t data)
{
uint8_t next_head = serial1_tx_buffer_head + 1;
if (next_head >= TX_RING_BUFFER1)
{
next_head = 0;
}
while (next_head == serial1_tx_buffer_tail);
serial1_tx_buffer[serial1_tx_buffer_head] = data;
serial1_tx_buffer_head = next_head;
USART1- >CR1 |= USART_FLAG_TXE;
}
int serial1_read(uint8_t *ch)
{
uint8_t tail = serial1_rx_buffer_tail;
if (serial1_rx_buffer_head == tail)
{
return 0;
}
else
{
uint8_t data = serial1_rx_buffer[tail];
tail++;
if (tail >= RX_RING_BUFFER1)
{
tail = 0;
}
serial1_rx_buffer_tail = tail;
*ch = data;
return 1;
}
}
void storeHandleDataIn(uint8_t data)
{
uint8_t next_head;
next_head = serial1_rx_buffer_head + 1;
if (next_head >= RX_RING_BUFFER1)
{
next_head = 0;
}
if (next_head != serial1_rx_buffer_tail)
{
serial1_rx_buffer[serial1_rx_buffer_head] = data;
serial1_rx_buffer_head = next_head;
}
else
{
next_head++;
next_head--;
}
}
之后我們就可以編寫一個/dev/ttyS1設備文件用于串口1的驅動:
int ttyS1_open(struct file *fs)
{
return 0;
}
int ttyS1_close(struct file *fs)
{
return 0;
}
size_t ttyS1_read(struct file *fs, void *buff, size_t size)
{
uint8_t *p = (uint8_t *)buff;
size_t read_len = 0;
for (int i = 0; i < size; i++)
{
if (!serial1_read(&p[read_len]))
{
return read_len;
}
read_len++;
}
return size;
}
size_t ttyS1_write(struct file *fs, const void *buff, size_t size)
{
uint8_t *p = (uint8_t *)buff;
for (int i = 0; i < size; i++)
{
serial1_write(p[i]);
}
return size;
}
void ttyS1_init(void)
{
file_operations_s ops = {0};
ops.open = ttyS1_open;
ops.close = ttyS1_close;
ops.write = ttyS1_write;
ops.read = ttyS1_read;
ops.ioctl = NULL;
fs_register_dev("/dev/ttyS1", ops);
}
-
處理器
+關注
關注
68文章
19349瀏覽量
230296 -
計算機
+關注
關注
19文章
7520瀏覽量
88233 -
Unix系統
+關注
關注
0文章
15瀏覽量
9680 -
Cortex-M3
+關注
關注
9文章
270瀏覽量
59509 -
串口輸出
+關注
關注
0文章
16瀏覽量
7513
發布評論請先 登錄
相關推薦
力天手把手教你學單片機視頻全集下載
小七免殺論壇vip 2013源碼免殺培訓課程
【連載貼】【NetRotuer之像學單片機一樣學linux筆記】一、目錄篇
【IMX6UL開發板試用體驗】- 項目前篇 - USB 設備驅動實戰篇
【電子書】《HELLO FPGA》- 項目實戰篇
如何開發符合AUTOSAR規范的電機控制器軟件
Linux和RTOS的時鐘和定時器怎么使用
觸摸按鍵控制LED學習筆記
HELLO FPGA項目實戰篇的PDF電子書免費下載
![HELLO FPGA項目<b class='flag-5'>實戰篇</b>的PDF電子書免費下載](https://file.elecfans.com/web1/M00/BD/E8/pIYBAF7UuQ2APphwAAC0SUKXacM716.png)
【單片機】實戰篇:Keil+Proteus數碼管計數99
![【單片機】<b class='flag-5'>實戰篇</b>:Keil+Proteus數碼管計數99](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
INTEL FPGA學習筆記
![INTEL FPGA學習筆記](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評論