最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網 會員登陸 & 注冊

06-對 part4-miniuart 的搬運和翻譯

2023-08-11 17:40 作者:Xdz-2333  | 我要投稿

非黑色字體均為我自己添加,原文放在末尾

地址映射 IO

????????我們擁有并且運行了"Hello world"的例子.讓我們來花點時間解釋一下 io.c 中用到的,把消息通過UART傳送到我們的開發(fā)設備上的概念.

????????我們從UART開始的原因是 --?它是一個(相對)簡單的硬件,因為它使用內存映射I/O (MMIO).這意味著我們可以通過對樹莓派上一組預先確定好的地址進行讀寫來與硬件進行溝通.我們可以對不同的地址進行寫入來影響樹莓派不同的行為.

????????對于內存地址映射的補充:

????????CPU實際上只能做三件事:讀數(shù)據,運算,寫數(shù)據,因此CPU想要控制其他的部分要么有專用的指令,要么向特定的地址寫入或者讀取數(shù)據.如果讀者們讀過CSAPP或者對計算機中"抽象"這個概念有所了解,那么應該就能很好的理解這中方式.各種外圍部件隱藏自己的實現(xiàn)細節(jié),只對CPU以寄存器的設置暴露接口,降低系統(tǒng)耦合性,方便操作系統(tǒng)移植等.

????????這些內存地址從 0xFE000000 (我們的 PERIPHERAL_BASE?)

????????注意:你可能好奇為什么基地址與樹莓派官網文檔里面的不同(https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf).這是因為樹莓派啟動時默認進入 Low Peripheral Mode.這將把外設映射到內存的最后64mb,因此它們能"被arm 從 0xFEnn_nnnn看見".(mini UART 在樹莓派的手冊里也有,和UART不是一個東西)

????????人們可能希望啟動 High Peripheral Mode?(完全的 35-bit 地址映射)從而避免"丟失"內存的最后64mb地址.這有許多副作用,然而,做到這點需要對內核進行重構(即使是在這個簡單的教程中)來使其工作.

設置GPIO(通用輸入/輸出)引腳

????????GPIO引腳(記得嗎 -- 我們的USB轉TTL就用的它)使用了MMIO. io.c 頂部的那節(jié)(用 //GPIO 做的標記) 實現(xiàn)了一些函數(shù)來對這些引腳進行設置.

????????在這一點上,我推薦對樹莓派官方手冊進行深挖(見上方的鏈接).它有詳細的GPIO的部分.只是不要相信你讀到的所有內容,因為目前這份文檔中有很多錯誤.

????????然而它會告訴你我們的內存映射到的GPIO寄存器都做了什么,比如 GPFSEL0 ,GPSET0,GPCRL0和 GPPUPPDN0 .這些都是從?PERIPHERAL_BASE?開始的已知的偏移量,在第一個enum中定義.

????????mmio_read和mmio_write兩個函數(shù)可以用來從這些寄存器中讀寫數(shù)據.

關于GPIO引腳

????????記得我們說過計算機使用0和1通信嗎?我們想做的其中一件事就是設置一個引腳為高(二進制的 1 ),或者清空一個引腳(二進制的 0 ).gpio_set和gpio_clear這兩個函數(shù)就是做這個的.相應的引腳會接收一個高電平,當它被設置為高,反之當它設置為低.

????????然而,引腳可以處于三種pull?狀態(tài)之一.這將告訴樹莓派這個引腳的默認狀態(tài).如果一個引腳被設定為"上拉",然后它的靜止狀態(tài)就是高電平(接受電壓)除非另有說明.如果它被設定為"下拉",然后它的靜止狀態(tài)就是低電平.這在連接不同的設備中很有用.如果一個引腳被設置為 "Pull?None",我們就說它被"懸空"了,這就是我們的UART所需要的.gpio_pull函數(shù)設定給定引腳的狀態(tài).

????????關于GPIO的一些補充:

????????如果讀者學過數(shù)電或者模電,應該不難理解這里的引腳狀態(tài),然而還是需要進行一下補充,實際引腳上除了上拉,下拉,浮空,還有高阻態(tài),推挽輸出,開漏等狀態(tài),這些對應了不同的晶體管和電阻電容等的連接以及設置.這里的引腳抽象概念比晶體管,電阻等電路更高一級.它隱藏了0和1物理實現(xiàn),并使得邏輯上的0和1有可靠的物理保證.

????????然而為了順利使用引腳進行輸入輸出,我們還是需要一些關于各種狀態(tài)的知識.

????????首先是引腳是有極限的,一般情況下引腳工作電流都是uA量級,特殊的引腳有mA量級,這就意味這如果引腳如果使用不當(舉個極端例子,給空調供電),要么引腳會燒掉,要么無法達到要求的電平.引腳的耐壓值也是有限的,有的只有3.3V耐壓,而有的有5V.

????????其次在這里對這些狀態(tài)做個簡單解釋,上拉就是輸出高電平,下拉就是輸出低電平,浮空取決于引腳連線上電壓,一般用于輸入,高阻態(tài)就是不導通,推挽輸出一般用于PWM驅動等,開漏的高電平取決于連線(如果引腳耐壓值為5V,而上拉電平為3.3V,改變狀態(tài)為開漏輸出后再外接合適的電阻,就可以把高電平調為5V).拉電流是指電流從晶體管往外流,灌電流是指外面電流流向晶體管.

????????關于GPIO的你需要知道的更多的一些事:

  1. 樹莓派的可以容納比現(xiàn)有的硬件引腳更多的函數(shù)

  2. 為了解決這個問題,我們可以將引腳動態(tài)映射到函數(shù)上

  3. 在我們的例子中,我們希望GPIO 14 和GPIO 15 采用函數(shù)5 (分別為TXD1和RXD1)

  4. 這將把樹莓派的miniUART映射到我們連接了線的引腳上

  5. 我們調用gpio_function 來設置這些

????????現(xiàn)在io.c的GPIO 部分應該已經清楚了.我們繼續(xù).

設置UART

????????io.c的第二節(jié)(由UART標記)實現(xiàn)了一些函數(shù)來幫助我們和UART交流.這個裝置同樣使用MMIO,并且你可以再次看到寄存器設置,就像你第一次看到的那樣.去看樹莓派的手冊(見上方鏈接)來了解寄存器更詳細的解釋.

????????我只是想調用AUX_UART_CLOCK參數(shù),我們將其設置為500000000。還記得我說過UART通信是關于時間的嗎?好吧,這與我們在config.txt中添加core_freq_min=500行時設置的時鐘速度(500 MHz)完全相同。這不是巧合!

????????這里應該是采用輪詢方法,所以要設置CPU速率,一般訪問外設的方法有三種,輪詢,中斷,DMA,具體可自行百度.

????????你會注意到uart_init()函數(shù)中其他熟悉的數(shù)值,我們直接在kernel.c的main()例程中調用它們.我們設置波特率為115200,數(shù)據位為8

????????最后我們加上了這些有用的函數(shù):

  • uart_isWriteByteReady -- 檢查串口狀態(tài)保證我們已經"準備好發(fā)送"

  • uart_writeByteBlockingActual -- 等待直到我們 "準備好發(fā)送" 然后發(fā)送一個字符

  • uart_writeText -- 使用?uart_writeByteBlockingActual 發(fā)送字符

????????你應該記得 uart_writeText 是我們從main() 中調用來打印 "Hello World"的函數(shù)!

一些其他代碼

????????我不希望這個教程只是一個簡單的解釋,所以在代碼中,你會看見我往io.c中添加了更多功能,并且在內核中使用.通讀看看你是否能理解發(fā)生了什么,如果你需要,去翻文檔.

????????現(xiàn)在我們可以從串口中讀數(shù)據了.如果你編譯這個內核并且像之前那樣啟動樹莓派,它將再次打印出Hello World.但是,在此之后你可以像終端中輸入然后讓樹莓派發(fā)回給你.

????????現(xiàn)在我們可以進行雙向通信了.

????????我們同樣為我們的串口通信實現(xiàn)了一個軟件 FIFO 緩沖(https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)).樹莓派只給了到達串口的數(shù)據有限的緩沖空間,合并到我們自己的緩沖區(qū)可以對將要到來的數(shù)據進行更好的管理.

????????

Writing a "bare metal" operating system for Raspberry Pi 4 (Part 4)


Memory-Mapped I/O

We have our "Hello world!" example up and running. Let's just take a little time to explain the concepts that _io.c_ is using to send this message over the UART to our dev machine.


We started with the UART for a reason - it's a (relatively) simple piece of hardware to talk to because it uses **memory-mapped I/O** (MMIO). That means we can talk directly to the hardware by reading from and writing to a set of predetermined memory addresses on the RPi4. We can write to different addresses to influence the hardware's behaviour in different ways.


These memory addresses start at `0xFE000000` (our `PERIPHERAL_BASE`).


Note: you might wonder why this base address differs from the one shown throughout the [BCM2711 ARM Peripherals document](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf). It's because the RPi4 boots into Low Peripheral Mode by default. This maps the peripherals over the last 64mb of RAM, therefore they're "visible to the ARM at 0x0_FEnn_nnnn".


People might wish to enable High Peripheral mode (full 35-bit address map) so as to avoid "losing" that last 64mb of RAM. There are various side effects, however, of doing this and it would require some refactoring of the kernel (even in this simple tutorial) to make it work.


Configuring the GPIO (General Purpose Input/Output) pins

The GPIO pins (remember - we connected our USB to serial TTL cable to these) use MMIO. The top section of _io.c_ (marked with `// GPIO`) implements a few functions to configure these pins.


At this point, I recommend digging into the [BCM2711 ARM Peripherals document](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf). It has a very detailed section on GPIO. Just don't believe everything you read as there are plenty of mistakes in this document at the moment.


It will, however, tell you what our memory-mapped GPIO **registers** like `GPFSEL0`, `GPSET0`, `GPCLR0` and `GPPUPPDN0` do. These are all at known offsets from the `PERIPHERAL_BASE` and are defined by our first `enum`.


The two functions `mmio_read` and `mmio_write` can be used to read a value from and write a value to these registers.


About the GPIO pins

Remember how we said that computers communicate in 1's and 0's? One thing we might want to do is to **set a pin** high (binary 1) or **clear a pin** low (binary 0). The two functions `gpio_set` and `gpio_clear` do just this. The corresponding hardware pin will receive a voltage when it is set high, and not when it cleared low.?


That said, however, pins can also have one of three **pull states**. This tells the RPi4 what the default state of a pin is. If a pin is set to "Pull Up", then its resting state is high (receiving voltage) unless it's told otherwise. If a pin is set to "Pull Down", then its resting state is low. This can be useful for connecting different types of devices. If a pin is set to "Pull None" then it is said to be "floating", and this is what our UART needs. The `gpio_pull` function sets the pull state of a given pin for us.


You need to know just a few more things about the GPIO pins:


?* The RPi4 is capable of more functions than there are hardware pins available for

?* To solve this, our code can dynamically map a pin to a function

?* In our case, we want GPIO 14 and GPIO 15 to take alternate function 5 (TXD1 and RXD1 respectively)

?* This maps the RPi4's mini UART (UART1) to the pins we connected our cable to!

?* We use the `gpio_function` call to set this up


Now the GPIO section of _io.c_ should be clear. Let's move on.


Configuring the UART

The second section of _io.c_ (marked with `// UART`) implements a few functions to help us talk to the UART. Thankfully, this device also uses MMIO, and you'll see the registers set up in the first `enum` just like you saw before. Look in the [BCM2711 ARM Peripherals document](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf) for a more detailed explanation of these registers.


I do just want to call out the `AUX_UART_CLOCK` parameter, which we set to `500000000`. Remember how I said that UART communication is all about timing? Well, this is exactly the same clock speed (500 MHz) that we set in _config.txt_ when we added the `core_freq_min=500` line. This is no coincidence!


You'll also note some other familiar numbers in the `uart_init()` function, which we call directly from our `main()` routine in _kernel.c_. We set the baud rate to `115200`, and the number of bits to `8`.


Finally we add some useful functions:


?* `uart_isWriteByteReady` - checks the UART line status to ensure we are "ready to send"

?* `uart_writeByteBlockingActual` - waits until we are "ready to send" and then sends a single character

?* `uart_writeText` - sends a whole string using `uart_writeByteBlockingActual`?


You'll remember that `uart_writeText` is what we call from `main()` to print "Hello world!".


Some extra code

I don't want this tutorial to just be an explanation so, in the code, you'll see I've added some more functionality to _io.c_ and made use of it in our kernel. Have a read through and see if you can understand what's going on. Refer to the documentation again if you need to.


We can now read from our UART too! If you build the kernel and power on the RPi4 just like before, it'll say hello to the world again. But, after that, you can type into the terminal emulator window and the RPi4 sends the characters right back to you.


_Now we're communicating in two directions!_


We also implemented a software [FIFO buffer](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)) for our UART communication. The RPi4 has limited buffer space for data arriving on the UART, and incorporating our own is likely to make it easier to manage incoming data in future.



06-對 part4-miniuart 的搬運和翻譯的評論 (共 條)

分享到微博請遵守國家法律
子洲县| 望城县| 视频| 象山县| 深州市| 安龙县| 历史| 武隆县| 屏边| 桑日县| 唐河县| 当阳市| 大竹县| 榆社县| 沛县| 汽车| 霍山县| 鲜城| 砚山县| 滕州市| 中山市| 利川市| 中宁县| 灵璧县| 区。| 深水埗区| 新闻| 平阴县| 蒙城县| 台东市| 河南省| 黄浦区| 洪雅县| 镇康县| 泽库县| 新疆| 嘉善县| 温州市| 赣州市| 富裕县| 张家口市|