首頁 > 評(píng)測(cè) > RTT-Studio環(huán)境下的APM32外設(shè)驅(qū)動(dòng)使用指南

RTT-Studio環(huán)境下的APM32外設(shè)驅(qū)動(dòng)使用指南

  
  • 作者:
  • 來源:
  • [導(dǎo)讀]
  • 本帖最后由 luobeihai 于 2023-4-19 00:08 編輯 #申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂 1. 簡介由于自己平時(shí)在項(xiàng)目中經(jīng)常使用 APM32 系列的 MCU,而且經(jīng)常是和 RT-Thread 系統(tǒng)一起使用,慢慢的就對(duì) APM32 系列 MCU 的

本帖最后由 luobeihai 于 2023-4-19 00:08 編輯

#申請(qǐng)?jiān)瓌?chuàng)#  @21小跑堂


1. 簡介

由于自己平時(shí)在項(xiàng)目中經(jīng)常使用 APM32 系列的 MCU,而且經(jīng)常是和 RT-Thread 系統(tǒng)一起使用,慢慢的就對(duì) APM32 系列 MCU 的 RT-Thread 外設(shè)驅(qū)動(dòng)使用熟悉了起來。
目前,RTT-Studio 環(huán)境已經(jīng)支持了 APM32F0/F1/F4/E1/S1 系列的 MCU,每個(gè)系列 MCU 的 RTT驅(qū)動(dòng)使用方法基本相似。下面我就以 APM32F407 這款 MCU 為例,介紹下在 RTT-Studio 環(huán)境下如何使用 APM32 的外設(shè)驅(qū)動(dòng),希望能幫助到大家快速把 APM32 的 RT-Thread 驅(qū)動(dòng)使用起來。
對(duì)于每個(gè)外設(shè)驅(qū)動(dòng)的使用和測(cè)試,大家可以配合 RTT 的官方文檔中心(https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/README)的設(shè)備和驅(qū)動(dòng)模塊進(jìn)行閱讀,文檔中心對(duì)于每一個(gè)驅(qū)動(dòng)如何使用和測(cè)試都有詳細(xì)的介紹。

2. 新建RTT-Studio工程

下面是以 APM32F407 進(jìn)行介紹,所以需要先安裝 APM32F4 最新的 RTT-Studio 軟件支持包。

2.1 下載APM32F4軟件支持包

打開 SDK 管理器,然后找到 APM32F4 的軟件支持包,然后點(diǎn)擊安裝即可。

2.2 基于芯片新建一個(gè) APM32F407 的工程

控制臺(tái)串口默認(rèn)是UART1,如果想要更改控制臺(tái)串口號(hào),下面會(huì)詳細(xì)介紹,這里先默認(rèn)UART1。然后點(diǎn)擊完成即可創(chuàng)建一個(gè) APM32F407 的工程。

3. apm32_msp.c 文件介紹

新建完工程之后,驅(qū)動(dòng)目錄結(jié)構(gòu)如下:
可以看到,已經(jīng)支持了大部分的外設(shè)驅(qū)動(dòng)了,如 can/flash/eth/pwm/sdio 等等外設(shè)驅(qū)動(dòng)。其中有一個(gè)文件是比較重要的, apm32_msp.c 文件,打開這個(gè)文件就可以了解到這個(gè)文件的作用就是初始化外設(shè)時(shí)鐘和 GPIO 口。比如用到 spi 外設(shè),那么我們就必須要在這個(gè)文件里面添加你自己板子所使用到的 spi 外設(shè) GPIO 口的初始化。
我們?cè)诤竺嬗泻芏嘤玫降耐庠O(shè)驅(qū)動(dòng),關(guān)于外設(shè)引腳的初始化代碼,都可以編寫放進(jìn)這個(gè)文件中。官方也已經(jīng)給出了這些外設(shè)的部分引腳的初始化代碼了,如果我們所使用的硬件引腳和給出的代碼是一致的,那么就不需要更改給出的示例代碼,如果所使用的引腳不一樣,就需要自己編寫引腳的初始化代碼了。
為什么編寫驅(qū)動(dòng)的時(shí)候,沒有把外設(shè) GPIO 口和對(duì)應(yīng)外設(shè)的時(shí)鐘初始化都編寫到對(duì)應(yīng)的驅(qū)動(dòng)程序中呢?這樣用戶在使用時(shí)不就可以編寫更少的代碼了嗎?
關(guān)于這一點(diǎn),主要是因?yàn)槊總(gè)人所使用的硬件是不一樣的,比如說有的人可能使用的是 spi1 外設(shè),也可能使用 spi2 外設(shè),而且就算是 spi1 外設(shè),所使用的引腳也可能不同,因?yàn)?spi1 外設(shè)的 GPIO 引腳很可能有多個(gè)引腳可選。簡單來說就是關(guān)于這部分代碼的不確定性太大了,如果在驅(qū)動(dòng)程序中把所有的可能都寫出來,會(huì)導(dǎo)致驅(qū)動(dòng)程序太過復(fù)雜和冗余。所以人家在編寫這部分驅(qū)動(dòng)代碼的時(shí)候,把 GPIO 引腳初始化的代碼給剝離出來了,讓使用者自己去編寫這部分代碼。
當(dāng)然有一些簡單的外設(shè)驅(qū)動(dòng),也可以把引腳的初始化相關(guān)的代碼給寫到驅(qū)動(dòng)程序中去。APM32的外設(shè)驅(qū)動(dòng),如 adc/dac/hwtimer 這些驅(qū)動(dòng),底層引腳初始化和外設(shè)時(shí)鐘的開啟就是寫到了驅(qū)動(dòng)程序里面的。

4. UART設(shè)備

新建完工程之后,默認(rèn)是已經(jīng)打開了UART和PIN設(shè)備的,因?yàn)檫@兩個(gè)外設(shè)是最常用的。只要編譯下載,就可看到串口打印的輸出信息。
對(duì)于這兩個(gè)外設(shè)的 API 如何使用,可以參考RTT的官方文檔,這里不再介紹。下面重點(diǎn)介紹的是如何修改控制臺(tái)的輸出串口,因?yàn)樵趯?shí)際的項(xiàng)目中,很多時(shí)候控制臺(tái)所使用的串口并不是 UART1 。

4.1 確定串口引腳,修改串口GPIO初始化代碼

我們根據(jù)原理圖,確定自己所使用的串口調(diào)試具體是哪個(gè)串口,和對(duì)應(yīng)的串口引腳。然后我們?cè)?apm32_msp.c 文件中,添加串口引腳的初始化代碼即可。比如,我的板子就用的UART6,那我們?cè)谶@個(gè)文件添加UART6的引腳初始化代碼。
官方所提供的示例代碼中,已經(jīng)提供了 UART1/UART2 的引腳初始化了,我們?cè)?apm32_msp.c 文件中的apm32_usart_init 函數(shù)添加 UART6 的引腳初始化代碼:
  1. void apm32_usart_init(void)
  2. {
  3.     GPIO_Config_T GPIO_ConfigStruct;
  4. #ifdef BSP_USING_UART6
  5.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);
  6.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART6);
  7.  
  8.     GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_6, GPIO_AF_USART6);
  9.     GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_7, GPIO_AF_USART6);
  10.  
  11.     GPIO_ConfigStruct.pin = GPIO_PIN_6 | GPIO_PIN_7;
  12.     GPIO_ConfigStruct.mode = GPIO_MODE_AF;
  13.     GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
  14.     GPIO_ConfigStruct.pupd = GPIO_PUPD_UP;
  15.     GPIO_ConfigStruct.speed = GPIO_SPEED_50MHz;
  16.     GPIO_Config(GPIOC, &GPIO_ConfigStruct);
  17. #endif
  18. }
復(fù)制代碼

4.2 在board.h中修改UART端口號(hào)

當(dāng)然,如果我們?cè)谛陆üこ虝r(shí),就已經(jīng)選擇了UART6作為控制臺(tái)的串口,那么我們只需要做完第一步,把所需要的UART引腳進(jìn)行初始化即可,下面的步驟就都不需要做了。

4.3 在 rtconfig.h 文件中修改控制臺(tái)所需要的串口設(shè)備

修改完之后,重新編譯下載,然后可以看到串口打印信息如下:
而且,也可以看到串口設(shè)備變?yōu)?uart6 了。

5. ADC設(shè)備
5.1 使能ADC外設(shè)驅(qū)動(dòng)

打開 RT-Thread Setting 配置文件,然后找到 組件 -> 設(shè)備驅(qū)動(dòng)程序 -> 使用ADC設(shè)備驅(qū)動(dòng)程序,開啟該驅(qū)動(dòng)程序即可。

5.2 在board.h文件中定義ADC宏

board.h文件,都是定義和板級(jí)硬件所使用的宏定義,如下我們定義 BSP_USING_ADC1這個(gè)宏。

5.3 ADC設(shè)備測(cè)試方法

關(guān)于ADC設(shè)備的測(cè)試和使用示例,可以查閱RTT的文檔。下面我簡單介紹下,怎么測(cè)試我們的ADC外設(shè)驅(qū)動(dòng)是否正常工作了。
在命令行終端,可以輸入adc命令,然后有一下的使用方法:
  1. adc probe <adc_name>   - probe adc by name
  2. adc read <channel>     - read adc value on the channel
  3. adc disable <channel>  - disable adc channel
  4. adc enable <channel>   - enable adc channel
復(fù)制代碼

我們對(duì)照著輸入上面的命令,即可獲取ADC采樣的電壓值。
我們使用的是adc1外設(shè),然后adc1外設(shè)通采樣道對(duì)應(yīng)的是哪個(gè)GPIO引腳,大家可以查看 APM32F407 的數(shù)據(jù)手冊(cè),當(dāng)然也可稱查看ADC外設(shè)的驅(qū)動(dòng)代碼,如下:
然后,我們?cè)诖诮K端輸入下面的命令,讀取PA5(通道5)口的電壓值。
可以看到,PA5引腳接3.3V和GND讀取到的數(shù)值是不一樣的,當(dāng)然這個(gè)數(shù)據(jù)還需要根據(jù)12位的分辨率,然后進(jìn)行電壓值的轉(zhuǎn)換。計(jì)算公式:vol = value * 3300 / (1<<12)

6. PWM設(shè)備
6.1 使能PWM外設(shè)驅(qū)動(dòng)

在 RT-Thread Setting 配置文件中使能PWM外設(shè)驅(qū)動(dòng)。

6.2 在board.h文件中定義PWM外設(shè)相關(guān)的宏

這里,我打算使用定時(shí)器3的通道1進(jìn)行PWM輸出,所有定義PWM3這個(gè)宏。

6.3 在apm32_msp.c文件中添加定時(shí)器通道初始化代碼

由于人家驅(qū)動(dòng)程序并不清楚你的硬件使用的是哪個(gè)定時(shí)器哪個(gè)通道,所以把這部分的初始化程序剝離出來留給用戶去寫了(因?yàn)锳DC外設(shè)驅(qū)動(dòng)比較簡單,所以ADC的引腳的初始化寫進(jìn)了驅(qū)動(dòng)程序中)。
我使用的是PC6引腳輸出PWM,就是定時(shí)器3的通道1。關(guān)于該通道初始化代碼,剛好官方提供的初始化代碼就有這個(gè),所以不用重新寫了。但是如果你用的不是這個(gè)定時(shí)器通道,就必須添加該部分初始化代碼。
  1. void apm32_msp_timer_init(void *Instance)
  2. {
  3. #ifdef BSP_USING_PWM3
  4.     GPIO_Config_T gpio_config;
  5.     TMR_T *tmr_x = (TMR_T *)Instance;
  6.  
  7.     if (tmr_x == TMR3)
  8.     {
  9.         RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);
  10.         RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
  11.  
  12.         /* TMR3 channel 1 gpio init */
  13.         GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_6, GPIO_AF_TMR3);
  14.         gpio_config.pin = GPIO_PIN_6;
  15.         gpio_config.mode = GPIO_MODE_AF;
  16.         gpio_config.otype = GPIO_OTYPE_PP;
  17.         gpio_config.speed = GPIO_SPEED_50MHz;
  18.         GPIO_Config(GPIOC, &gpio_config);
  19.  
  20.         /* TMR3 channel 2 gpio init */
  21.         GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_7, GPIO_AF_TMR3);
  22.         gpio_config.pin = GPIO_PIN_7;
  23.         GPIO_Config(GPIOC, &gpio_config);
  24.  
  25.         /* TMR3 channel 3 gpio init */
  26.         GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_8, GPIO_AF_TMR3);
  27.         gpio_config.pin = GPIO_PIN_8;
  28.         GPIO_Config(GPIOC, &gpio_config);
  29.  
  30.         /* TMR3 channel 4 gpio init */
  31.         GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_9, GPIO_AF_TMR3);
  32.         gpio_config.pin = GPIO_PIN_9;
  33.         GPIO_Config(GPIOC, &gpio_config);
  34.     }
  35. #endif
  36. }
復(fù)制代碼

6.4 PWM設(shè)備使用示例

詳細(xì)的PWM設(shè)備使用示例,我們可以參考RTT的官方文檔。
下面的代碼是從RTT文檔中復(fù)制過來的,PWM輸出的引腳與LED相連,可以觀察到 LED 燈不停的由暗變到亮,然后又從亮變到暗。
  1. /*
  2. * 程序清單:這是一個(gè) PWM 設(shè)備使用例程
  3. * 例程導(dǎo)出了 pwm_led_sample 命令到控制終端
  4. * 命令調(diào)用格式:pwm_led_sample
  5. * 程序功能:通過 PWM 設(shè)備控制 LED 燈的亮度,可以看到LED不停的由暗變到亮,然后又從亮變到暗。
  6. */
  7.  
  8. #include <rtthread.h>
  9. #include <rtdevice.h>
  10.  
  11. #define PWM_DEV_NAME        "pwm3"  /* PWM設(shè)備名稱 */
  12. #define PWM_DEV_CHANNEL     4       /* PWM通道 */
  13.  
  14. struct rt_device_pwm *pwm_dev;      /* PWM設(shè)備句柄 */
  15.  
  16. static int pwm_led_sample(int argc, char *argv[])
  17. {
  18.     rt_uint32_t period, pulse, dir;
  19.  
  20.     period = 500000;    /* 周期為0.5ms,單位為納秒ns */
  21.     dir = 1;            /* PWM脈沖寬度值的增減方向 */
  22.     pulse = 0;          /* PWM脈沖寬度值,單位為納秒ns */
  23.  
  24.     /* 查找設(shè)備 */
  25.     pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
  26.     if (pwm_dev == RT_NULL)
  27.     {
  28.         rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME);
  29.         return RT_ERROR;
  30.     }
  31.  
  32.     /* 設(shè)置PWM周期和脈沖寬度默認(rèn)值 */
  33.     rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
  34.     /* 使能設(shè)備 */
  35.     rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
  36.  
  37.     while (1)
  38.     {
  39.         rt_thread_mdelay(50);
  40.         if (dir)
  41.         {
  42.             pulse += 5000;      /* 從0值開始每次增加5000ns */
  43.         }
  44.         else
  45.         {
  46.             pulse -= 5000;      /* 從最大值開始每次減少5000ns */
  47.         }
  48.         if (pulse >= period)
  49.         {
  50.             dir = 0;
  51.         }
  52.         if (0 == pulse)
  53.         {
  54.             dir = 1;
  55.         }
  56.  
  57.         /* 設(shè)置PWM周期和脈沖寬度 */
  58.         rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
  59.     }
  60. }
  61. /* 導(dǎo)出到 msh 命令列表中 */
  62. MSH_CMD_EXPORT(pwm_led_sample, pwm sample);
復(fù)制代碼

我們把該代碼,復(fù)制到main.c文件中,然后編譯下載。然后再串口終端輸入 pwm_led_sample 這個(gè)命令,就可觀察到板子的LED燈不停的由暗到亮了(要確保PWM輸出的引腳與LED的引腳相連了)。

7. SPI設(shè)備
7.1 使能SPI外設(shè)驅(qū)動(dòng)和串行Flash驅(qū)動(dòng)程序

我的硬件板子,SPI外設(shè)是連接了 SPI Flash 芯片的,所以我們?cè)陂_啟使能SPI外設(shè)驅(qū)動(dòng)時(shí),把SPI Flash驅(qū)動(dòng)程序也一起打開,如下:

7.2 在board.h文件中定義SPI相關(guān)的宏

我所使用到的是SPI1,所以開啟SPI1的宏定義。

7.3 在apm32_msp.c中編寫SPI外設(shè)引腳的初始化代碼

對(duì)于SPI驅(qū)動(dòng)程序,需要用戶自己去編寫對(duì)于的SPI外設(shè)引腳初始化代碼。我的板子所使用到的是SPI1的PB3/4/5引腳,所以相關(guān)初始化代碼如下:
  1. void apm32_msp_spi_init(void *Instance)
  2. {
  3. #if defined (BSP_USING_SPI1) || defined (BSP_USING_SPI2) || defined (BSP_USING_SPI3)
  4.     GPIO_Config_T gpioConfig;
  5.     SPI_T *spi_x = (SPI_T *)Instance;
  6.  
  7.     if(spi_x == SPI1)
  8.     {
  9.         /* Enable related Clock */
  10.         RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
  11.         RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SPI1);
  12.         RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
  13.  
  14.         /* Config SPI1 PinAF */
  15.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_3, GPIO_AF_SPI1);
  16.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_4, GPIO_AF_SPI1);
  17.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_5, GPIO_AF_SPI1);
  18.  
  19.         /* Config SPI GPIO, SCK=PB3, MISO=PB4, MOSI=PB5 */
  20.         GPIO_ConfigStructInit(&gpioConfig);
  21.         gpioConfig.pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
  22.         gpioConfig.speed = GPIO_SPEED_100MHz;
  23.         gpioConfig.mode = GPIO_MODE_AF;
  24.         gpioConfig.otype = GPIO_OTYPE_PP;
  25.         gpioConfig.pupd = GPIO_PUPD_NOPULL;
  26.         GPIO_Config(GPIOB, &gpioConfig);
  27.     }
  28. #endif
  29. }
復(fù)制代碼

如果你使用的是其他SPI引腳,編寫對(duì)于的SPI引腳初始化代碼即可。

7.4 掛載SPI設(shè)備

SPI 驅(qū)動(dòng)會(huì)注冊(cè) SPI 總線,SPI 設(shè)備需要掛載到已經(jīng)注冊(cè)好的 SPI 總線上。
SPI驅(qū)動(dòng)程序已經(jīng)提供好了  rt_spi_bus_attach_device 這個(gè)函數(shù)進(jìn)行SPI設(shè)備的掛載了。我們調(diào)用該函數(shù)就可以SPI Flash設(shè)備掛載到某個(gè)SPI總線上,代碼如下:
  1. #include <rtthread.h>
  2. #include "spi_flash.h"
  3. #include "spi_flash_sfud.h"
  4. #include "drv_spi.h"
  5.  
  6. static int rt_hw_spi_flash_init(void)
  7. {
  8.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOG);
  9.     rt_hw_spi_device_attach("spi1", "spi10", GPIOG, GPIO_PIN_8);
  10.  
  11.     if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
  12.     {
  13.         return -RT_ERROR;
  14.     }
  15.  
  16.     return RT_EOK;
  17. }
  18. INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
復(fù)制代碼

把上述代碼復(fù)制到 main.c 文件中(我這里只是為了測(cè)試驗(yàn)證,為了方便并沒有新建一個(gè)文件,而是直接復(fù)制到main.c文件),然后編譯下載程序,查看串口終端輸出信息如下:
可以看到,已經(jīng)發(fā)現(xiàn)了一個(gè)華邦的SPI Flash芯片,而且容量大小是 16M byte。

8. CAN設(shè)備

8.1 使能CAN外設(shè)驅(qū)動(dòng)

打開 RT-Thread Settings ,使能CAN設(shè)備驅(qū)動(dòng)程序。

8.2 在board.h定義與CAN外設(shè)相關(guān)的宏

RTT-Studio 自動(dòng)生成的 board.h 文件,還沒有CAN相關(guān)的宏定義,那么我們就自己定義CAN外設(shè)驅(qū)動(dòng)所需要的宏定義即可。定義如下的宏:
  1. #define BSP_USING_CAN1
  2. #define BSP_USING_CAN2
復(fù)制代碼

用到哪個(gè)CAN,就定義哪個(gè)就行。

8.3 在apm32_msp.c文件中編寫CAN引腳初始化代碼

同樣的,自己板子用到的是哪路CAN,哪個(gè)引腳,還需要在apm32_msp.c文件中編寫引腳相關(guān)的初始化代碼。下面的CAN引腳初始化代碼,是apm32_msp.c文件已有的示例代碼,如果你使用的CAN引腳,剛好是示例代碼所使用的引腳,那么你就不用更改代碼了。
  1. void apm32_msp_can_init(void *Instance)
  2. {
  3. #if defined(BSP_USING_CAN1) || defined(BSP_USING_CAN2)
  4.     GPIO_Config_T  GPIO_InitStructure;
  5.     CAN_T *CANx = (CAN_T *)Instance;
  6.  
  7.     if (CAN1 == CANx)
  8.     {
  9.         RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_CAN1);
  10.  
  11.         RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
  12.  
  13.         /* PB8: CAN1_RX, PB9: CAN1_TX */
  14.         GPIO_InitStructure.pin = GPIO_PIN_8 | GPIO_PIN_9;
  15.         GPIO_InitStructure.mode = GPIO_MODE_AF;
  16.         GPIO_InitStructure.otype = GPIO_OTYPE_PP;
  17.         GPIO_InitStructure.speed = GPIO_SPEED_100MHz;
  18.         GPIO_InitStructure.pupd = GPIO_PUPD_UP;
  19.         GPIO_Config(GPIOB, &GPIO_InitStructure);
  20.  
  21.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_8, GPIO_AF_CAN1);
  22.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_9, GPIO_AF_CAN1);
  23.     }
  24.     else if (CAN2 == CANx)
  25.     {
  26.         /* When using the CAN2 peripheral, the CAN1 clock must be turned on */
  27.         RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_CAN1);
  28.         RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_CAN2);
  29.  
  30.         RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);
  31.  
  32.         /* PB12: CAN2_RX, PB13: CAN2_TX */
  33.         GPIO_InitStructure.pin = GPIO_PIN_12 | GPIO_PIN_13;
  34.         GPIO_InitStructure.mode = GPIO_MODE_AF;
  35.         GPIO_InitStructure.otype = GPIO_OTYPE_PP;
  36.         GPIO_InitStructure.speed = GPIO_SPEED_100MHz;
  37.         GPIO_InitStructure.pupd = GPIO_PUPD_UP;
  38.         GPIO_Config(GPIOB, &GPIO_InitStructure);
  39.  
  40.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_12, GPIO_AF_CAN2);
  41.         GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_13, GPIO_AF_CAN2);
  42.     }
  43. #endif
  44. }
復(fù)制代碼

8.4 CAN設(shè)備使用示例

CAN設(shè)備驅(qū)動(dòng)的使用示例,可以參考RTT官方文檔 https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/can/can ,文檔有詳細(xì)的介紹。下面我就簡單介紹下使用步驟:
  • 首先把文檔中的示例代碼復(fù)制到main.c文件,然后編譯下載。
  • 使用CAN分析儀連接到你板子的CANH和CANL(我所使用的是創(chuàng)芯科技的CAN分析儀)。
  • 打開創(chuàng)芯科技的CAN分析儀上位機(jī)軟件,然后按照1M的波特率啟動(dòng)設(shè)備(因?yàn)镃AN驅(qū)動(dòng)程序默認(rèn)的波特率是1M,如果你修改了CAN驅(qū)動(dòng)程序中的波特率,那么CAN接收數(shù)據(jù)的上位機(jī)設(shè)置的波特率要與之對(duì)應(yīng))。
  • 在串口終端運(yùn)行 can_sample 這個(gè)命令。
  • 然后查看 CAN 上位機(jī)工具是否接收到了開發(fā)板發(fā)送的CAN數(shù)據(jù)。
  • 也可以提供CAN上位機(jī)發(fā)送數(shù)據(jù)到開發(fā)板,然后板子會(huì)把接收到的數(shù)據(jù)打印出來。
詳細(xì)的使用示例可以看RTT的文檔,我這里不再重復(fù)介紹了。

9. SDIO設(shè)備
9.1 使能SDIO外設(shè)驅(qū)動(dòng)以及FatFs

1、使能SDIO設(shè)備驅(qū)動(dòng)程序,有些要填寫的參數(shù),默認(rèn)值就行。
2、使能虛擬文件系統(tǒng)和FatFs。

9.2 在board.h文件中定義SDIO外設(shè)相關(guān)的宏

9.3 在apm32_msp.c文件中編寫SDIO引腳初始化代碼

對(duì)于SDIO外設(shè),使能的引腳基本都是固定的,所以 apm32_msp.c 給出的代碼就可以直接使用,不用修改引腳初始化相關(guān)的代碼。
  1. void apm32_msp_sdio_init(void *Instance)
  2. {
  3. #ifdef BSP_USING_SDIO
  4.     GPIO_Config_T  GPIO_InitStructure;
  5.  
  6.     /* Enable the GPIO Clock */
  7.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC | RCM_AHB1_PERIPH_GPIOD);
  8.  
  9.     /* Enable the SDIO Clock */
  10.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SDIO);
  11.  
  12.     /* Enable the SDIO peripheral reset */
  13.     RCM_EnableAPB2PeriphReset(RCM_APB2_PERIPH_SDIO);
  14.  
  15.     /* Configure the GPIO pin */
  16.     GPIO_InitStructure.pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
  17.     GPIO_InitStructure.mode = GPIO_MODE_AF;
  18.     GPIO_InitStructure.speed = GPIO_SPEED_50MHz;
  19.     GPIO_InitStructure.otype = GPIO_OTYPE_PP;
  20.     GPIO_InitStructure.pupd = GPIO_PUPD_UP;
  21.     GPIO_Config(GPIOC, &GPIO_InitStructure);
  22.  
  23.     GPIO_InitStructure.pin = GPIO_PIN_2;
  24.     GPIO_Config(GPIOD, &GPIO_InitStructure);
  25.  
  26.     GPIO_ConfigPinAF(GPIOC,GPIO_PIN_SOURCE_8, GPIO_AF_SDIO);
  27.     GPIO_ConfigPinAF(GPIOC,GPIO_PIN_SOURCE_9, GPIO_AF_SDIO);
  28.     GPIO_ConfigPinAF(GPIOC,GPIO_PIN_SOURCE_10, GPIO_AF_SDIO);
  29.     GPIO_ConfigPinAF(GPIOC,GPIO_PIN_SOURCE_11, GPIO_AF_SDIO);
  30.     GPIO_ConfigPinAF(GPIOC,GPIO_PIN_SOURCE_12, GPIO_AF_SDIO);
  31.     GPIO_ConfigPinAF(GPIOD,GPIO_PIN_SOURCE_2, GPIO_AF_SDIO);
  32.  
  33.     /* Disable the SDIO peripheral reset */
  34.     RCM_DisableAPB2PeriphReset(RCM_APB2_PERIPH_SDIO);
  35. #endif
  36. }
復(fù)制代碼

9.4 SD卡掛載elm FatFs文件系統(tǒng)

經(jīng)過上面的步驟之后,SDIO其實(shí)已經(jīng)正常工作起來了的。然后,我們把下面的代碼復(fù)制到 main.c 文件中。
  1. #include <dfs_elm.h>
  2. #include <dfs_fs.h>
  3. #include <dfs_file.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <sys/stat.h>
  7. #include <sys/statfs.h>
  8.  
  9. void sd_mount(void *parameter)
  10. {
  11.     while (1)
  12.     {
  13.         rt_thread_mdelay(500);
  14.         if(rt_device_find("sd0") != RT_NULL)
  15.         {
  16.             if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
  17.             {
  18.                 LOG_I("sd card mount to '/'");
  19.                 break;
  20.             }
  21.             else
  22.             {
  23.                 LOG_W("sd card mount to '/' failed!");
  24.             }
  25.         }
  26.     }
  27. }
  28.  
  29. int apm32_sdcard_mount(void)
  30. {
  31.     rt_thread_t tid;
  32.  
  33.     tid = rt_thread_create("sd_mount", sd_mount, RT_NULL,
  34.                            2048, RT_THREAD_PRIORITY_MAX - 2, 20);
  35.     if (tid != RT_NULL)
  36.     {
  37.         rt_thread_startup(tid);
  38.     }
  39.     else
  40.     {
  41.         LOG_E("create sd_mount thread err!");
  42.     }
  43.     return RT_EOK;
  44. }
  45. INIT_APP_EXPORT(apm32_sdcard_mount);
復(fù)制代碼

最后編譯下載程序,可以看到串口終端輸出如下信息:
說明SD卡已經(jīng)掛載到了根目錄 "/" 了。這時(shí),我們可以使用 ls/cat/cd 等等基本的文件系統(tǒng)操作命令。

10. ETH設(shè)備
10.1 使能LwIP堆棧

在 RT-Thread Settings ,使能網(wǎng)絡(luò)接口設(shè)備和LwIP堆棧。由于我的開發(fā)板沒有插路由器,而是直接插到電腦的網(wǎng)口了,所以把DHCP功能給關(guān)掉,使用靜態(tài)IP地址。默認(rèn)靜態(tài)IP是: 192.168.1.30 ?梢宰约焊。

10.2 在board.h中定義以太網(wǎng)相關(guān)的宏

我們?cè)赽oard.h中開啟以太網(wǎng)設(shè)備的宏定義,以及使能相關(guān)的PHY芯片宏定義。

10.3 在apm32_msp.c文件中添加ETH外設(shè)初始化代碼

使能了LwIP之后,我們還需要添加以太網(wǎng)外設(shè)引腳初始化相關(guān)的代碼。我們需要根據(jù)原理圖,確認(rèn)MCU和物理層芯片是通過 RMII 還是 MII 接口連接的,然后在apm32_msp.c 文件中添加對(duì)應(yīng)接口引腳的初始化代碼。我使用的是 RMII 接口,對(duì)應(yīng)引腳初始化代碼如下:
  1. void apm32_msp_eth_init(void *Instance)
  2. {
  3. #ifdef BSP_USING_ETH
  4.     GPIO_Config_T GPIO_ConfigStruct;
  5.  
  6.     /* Enable SYSCFG clock */
  7.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
  8.  
  9.     /* Enable GPIOs clocks */
  10.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA | RCM_AHB1_PERIPH_GPIOC | RCM_AHB1_PERIPH_GPIOG);
  11.  
  12.     /* MII/RMII Media interface selection */
  13.     SYSCFG_ConfigMediaInterface(SYSCFG_INTERFACE_RMII);
  14.  
  15.     /*********************** Ethernet pins configuration ***************************/
  16.     /*
  17.         ETH_MDIO -------------------------> PA2
  18.         ETH_MDC --------------------------> PC1
  19.         ETH_MII_RX_CLK/ETH_RMII_REF_CLK---> PA1
  20.         ETH_MII_RX_DV/ETH_RMII_CRS_DV ----> PA7
  21.         ETH_MII_RXD0/ETH_RMII_RXD0 -------> PC4
  22.         ETH_MII_RXD1/ETH_RMII_RXD1 -------> PC5
  23.         ETH_MII_TX_EN/ETH_RMII_TX_EN -----> PG11
  24.         ETH_MII_TXD0/ETH_RMII_TXD0 -------> PG13
  25.         ETH_MII_TXD1/ETH_RMII_TXD1 -------> PG14
  26.     */
  27.     /* Configure PC1, PC4 and PC5 */
  28.     GPIO_ConfigStruct.pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
  29.     GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
  30.     GPIO_ConfigStruct.mode  = GPIO_MODE_AF;
  31.     GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
  32.     GPIO_ConfigStruct.pupd  = GPIO_PUPD_NOPULL;
  33.  
  34.     GPIO_Config(GPIOC, &GPIO_ConfigStruct);
  35.     GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_1, GPIO_AF_ETH);
  36.     GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_4, GPIO_AF_ETH);
  37.     GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_5, GPIO_AF_ETH);
  38.  
  39.     /* Configure PG11, PG13 and PG14 */
  40.     GPIO_ConfigStruct.pin =  GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
  41.     GPIO_Config(GPIOG, &GPIO_ConfigStruct);
  42.     GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_11, GPIO_AF_ETH);
  43.     GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_13, GPIO_AF_ETH);
  44.     GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_14, GPIO_AF_ETH);
  45.  
  46.     /* Configure PA1, PA2 and PA7 */
  47.     GPIO_ConfigStruct.pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
  48.     GPIO_Config(GPIOA, &GPIO_ConfigStruct);
  49.     GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_1, GPIO_AF_ETH);
  50.     GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_ETH);
  51.     GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_7, GPIO_AF_ETH);
  52. #endif
  53. }
復(fù)制代碼

10.4 添加 phy 芯片復(fù)位函數(shù)

我們需要通過原理圖確認(rèn),自己硬件板子上的 phy 芯片的復(fù)位引腳具體是連接到了那個(gè) GPIO 口,當(dāng)然也有可能是 phy 芯片的復(fù)位引腳沒有連接到任何 GPIO 口,而是連接到了硬件的復(fù)位電路上,這個(gè)時(shí)候我們就不需要編寫該函數(shù)的任何內(nèi)容,但為了避免編譯報(bào)錯(cuò),還是要編寫一個(gè)  void phy_reset(void) 。
我的硬件板子 phy 復(fù)位引腳就沒有連接到任何的 GPIO 口,所以只需要編寫下面的空函數(shù)即可。
  1. void phy_reset(void)
  2. {
  3.         /* phy復(fù)位相關(guān)代碼 */
  4. }
復(fù)制代碼

10.5 網(wǎng)絡(luò)功能測(cè)試

做完上面的步驟之后,我們就可以編譯下載程序了。程序啟動(dòng)后可以在串口終端看到如下打印信息,輸入 ifconfig 命令可以打印網(wǎng)絡(luò)連接狀態(tài)和 IP 地址。
保證我們的電腦和開發(fā)板的IP地址在同一網(wǎng)段,然后我們?cè)陔娔X的命令行終端輸入ping命令,可以看到電腦可以ping通開發(fā)板。
然后我們?cè)诖诮K端輸入ping命令,去ping電腦的IP地址,如下:
也可以正常ping通電腦的IP,說明網(wǎng)絡(luò)功能已經(jīng)正常運(yùn)行起來了。

11. SDRAM設(shè)備
11.1 打開SDRAM相關(guān)的代碼

對(duì)于SDRAM的使用,并不用使能什么設(shè)備驅(qū)動(dòng)(RTT也沒有SDRAM設(shè)備框架),我們只需要在 board.h 文件中定義 BSP_USING_SDRAM 這個(gè)宏,把SDRAM相關(guān)的代碼開啟即可。

11.2 修改SDRAM時(shí)序參數(shù)以及行、列、大小等參數(shù)

在 drv_sdram.c 文件中,已經(jīng)提供了 SDRAM 的時(shí)序及 SDRAM 行列寬度,SDRAM大小等參數(shù)的初始化了。但是提供的那些參數(shù)可能不一定適用于你使用的 SDRAM芯片,我們需要根據(jù)SDRAM的芯片手冊(cè)參數(shù)進(jìn)行更改代碼。
APM32 的固件庫函數(shù)中,DMC_TimingConfig_T 這個(gè)結(jié)構(gòu)體就是時(shí)序結(jié)構(gòu)體,需要根據(jù)SDRAM芯片的特性進(jìn)行賦值,該結(jié)構(gòu)體定義如下:
  1. /**
  2. * [url=home.php?mod=space&uid=247401]@brief[/url] Timing config definition
  3. */
  4. typedef struct
  5. {
  6.     uint32_t    latencyCAS  : 2;       /*!< DMC_CAS_LATENCY_T */
  7.     uint32_t    tRAS        : 4;       /*!< DMC_RAS_MINIMUM_T */
  8.     uint32_t    tRCD        : 3;       /*!< DMC_DELAY_TIME_T */
  9.     uint32_t    tRP         : 3;       /*!< DMC_PRECHARGE_T */
  10.     uint32_t    tWR         : 2;       /*!< DMC_NEXT_PRECHARGE_T */
  11.     uint32_t    tARP        : 4;       /*!< DMC_AUTO_REFRESH_T */
  12.     uint32_t    tCMD        : 4;       /*!< DMC_ATA_CMD_T */
  13.     uint32_t    tXSR        : 9;       /*!< auto-refresh commands, can be 0x000 to 0x1FF */
  14.     uint16_t    tRFP        : 16;      /*!< Refresh period, can be 0x0000 to 0xFFFF */
  15. } DMC_TimingConfig_T;
復(fù)制代碼

然后還有一個(gè)初始化結(jié)構(gòu)體,主要適合SDRAM行列寬度,bank寬度有關(guān)的,我們也需要根據(jù)SDRAM芯片的特性對(duì)其進(jìn)行賦值,該結(jié)構(gòu)體定義如下:
  1. /**
  2. * [url=home.php?mod=space&uid=247401]@brief[/url] Config struct definition
  3. */
  4. typedef struct
  5. {
  6.     DMC_BANK_WIDTH_T        bankWidth;     /*!< Number of bank bits */
  7.     DMC_ROW_WIDTH_T         rowWidth;      /*!< Number of row address bits */
  8.     DMC_COL_WIDTH_T         colWidth;      /*!< Number of col address bits */
  9.     DMC_CLK_PHASE_T         clkPhase;      /*!< Clock phase */
  10.     DMC_TimingConfig_T      timing;        /*!< Timing */
  11. } DMC_Config_T;
復(fù)制代碼

我們要在 drv_sdram.c 和 drv_sdram.h 文件中,根據(jù)自己所使用的 SDRAM 芯片的特性,對(duì)時(shí)序結(jié)構(gòu)體和初始化結(jié)構(gòu)體進(jìn)行賦值,修改對(duì)應(yīng)的代碼,下面是 drv_sdram.c 文件中的 SDRAM_Init 函數(shù)部分代碼截圖,我們主要就是要修改下這部分代碼。

11.3 SDRAM測(cè)試

我們修改完 SDRAM 的時(shí)序結(jié)構(gòu)體和初始化結(jié)構(gòu)體的參數(shù)之后,編譯下載到板子運(yùn)行,可以看到下面的提示信息:
然后我們可以在串口終端中輸入SDRAM的讀寫測(cè)試命令, sdram_test,可以看到測(cè)試結(jié)果如下:
可以看到SDRAM讀寫測(cè)試成功,說明SDRAM就可以正常使用起來了。

12. 總結(jié)

以上就是對(duì)部分外設(shè)驅(qū)動(dòng)使用介紹了,基本上我們要使用 APM32 的某一個(gè)外設(shè)驅(qū)動(dòng),大致的步驟都是差不多的。
  • 首先使能某個(gè)外設(shè)驅(qū)動(dòng)設(shè)備
  • 在board.h文件中定義該外設(shè)驅(qū)動(dòng)相關(guān)的宏
  • 在apm32_msp.c文件中添加對(duì)應(yīng)外設(shè)驅(qū)動(dòng)的引腳初始化代碼(當(dāng)然有些簡單的外設(shè),已經(jīng)把引腳的初始化代碼寫到了外設(shè)驅(qū)動(dòng)中,不需要我們?cè)倬帉懥,我們可以查看?duì)應(yīng)外設(shè)驅(qū)動(dòng)代碼是否有把引腳的初始化代碼寫進(jìn)去了)
  • 根據(jù)RTT的官方的文檔介紹,添加外設(shè)驅(qū)動(dòng)的使用代碼。
上面并沒有把所有的外設(shè)驅(qū)動(dòng)使用都介紹完,還有一些沒有介紹的外設(shè)驅(qū)動(dòng)使用起來也比較簡單,這里就不一一介紹了。大家只要對(duì)照著上面的使用步驟,然后結(jié)合RTT的官方文檔介紹,基本都可以使用起來。



  • 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!

網(wǎng)友評(píng)論

  • 聯(lián)系人:巧克力娃娃
  • 郵箱:board@21ic.com
  • 我要投稿
  • 歡迎入駐,開放投稿

熱門標(biāo)簽
項(xiàng)目外包 more+