KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记3——串口Stdio实现

  • 一、介绍
    • 任务目标
  • 二、工程创建
  • 三、软件设计
    • 第一步,BSP构建
      • 1, 添加前面的pll_config文件
      • 2,创建irqn_vector.s
      • 3,将常用的寄存器和外设地址命名
      • 4,创建usb_uart.h和usb_uart.s
        • 4.1, usb_uart.h的创建与封装
        • 4.2, usb_uart.s的创建与封装
          • 4.2.1, 定义配置宏
          • 4.2.2 Buffer FIFO的定义
          • 4.2.3 文内寄存器的重定义指定
          • 4.2.4 初始化函数void init(void)
          • 4.2.5 void send_ch(uint8_t)函数的实现
          • 4.2.5 `uint8_t read_ch(void)`函数的实现
          • 4.2.5 引出结构体`usb_uart`
          • 4.2.6 ISR `void USART1_IRQHandler(void)`
    • 第二步,Support的构建
    • 第三步 测试
    • 第四步,编译、链接、下载、复位、运行,Ура!
  • 四、调试与验证
  • 五、体会
  • 六、参考文件

一、介绍

这个测试主要是在ARM核里运行。所以如果使用正点的板子或者其他的407的板子,都可以做出来。做了好几天出来。也是为了自己练习编程,也是为了留个笔记,以后用的时候可以找到。

Всё, давай начнём.

任务目标

1、 实现USB-UART的串口驱动。
2、支持STDIO,并通过串口调试工具进行交互

发送比较简单,就是有发送的需求的时候就用usart->dr直接发。但是接收数据的话,这个UART没有CTS和DTS等其他信号的辅助,就做一个缓冲区,只要有数据来了就用中断ISR将数据送到缓冲区。读的时候就把数据从缓冲区里读出去。缓冲区的操作是FIFO。
试验环境

环境 描述
MCU STM32F407VGT6
IDE KEIL
串口 USART1, Baud rate: 115200Hz
Pin TX: PA10,RX: PA9

未来在使用这个驱动的时候,会通过管程来操作。也就是说,不会出现多线程对这个驱动进行调用。

二、工程创建

创建流程是:
第一步:创建新项目,选择STM32F407VGT6。
第二步:RTE选择CMSIS-CORE,CMSIS-RTX5-Lib,Device-Startup,Compiler-I/O-STDIN和Compiler-I/O-STDOUT。

第三步:在工作文件夹下创建Code/Application,Code/BSP和Code/Support三个文件夹。
第四步:在魔法棒下:

  1. Target标签选中IRAM2(其实没有真的用,只是勾上,任性。)
  2. C/C++(AC6)标签,将Language C调成c11;在Include Path里添加第三步里建的3个文件夹。
  3. Asm标签,将Assembler Option的汇编器选中除了GUN Syntax的其他选项;在Include Path里添加第三步里建的3个文件夹。
  4. Debug标签,选中CMSIS-DAP,并点一下Setting并确认。

第五步:点击“品”那个按钮,把原来的Group删了,创建3个Group,名字和我们创建的3个文件夹一致。
这样,我们的工程配置就完成了。

三、软件设计

第一步,BSP构建

在这个试验中,我在BSP里面需要构建Application、BSP和Support这3个包。这里我就用mermaid简单画画,我就不去开enterprise architecture了。

#mermaid-svg-tsuGLoizLaYGopsj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-tsuGLoizLaYGopsj .error-icon{fill:#552222;}#mermaid-svg-tsuGLoizLaYGopsj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tsuGLoizLaYGopsj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-tsuGLoizLaYGopsj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tsuGLoizLaYGopsj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tsuGLoizLaYGopsj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tsuGLoizLaYGopsj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tsuGLoizLaYGopsj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tsuGLoizLaYGopsj .marker.cross{stroke:#333333;}#mermaid-svg-tsuGLoizLaYGopsj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tsuGLoizLaYGopsj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tsuGLoizLaYGopsj .cluster-label text{fill:#333;}#mermaid-svg-tsuGLoizLaYGopsj .cluster-label span{color:#333;}#mermaid-svg-tsuGLoizLaYGopsj .label text,#mermaid-svg-tsuGLoizLaYGopsj span{fill:#333;color:#333;}#mermaid-svg-tsuGLoizLaYGopsj .node rect,#mermaid-svg-tsuGLoizLaYGopsj .node circle,#mermaid-svg-tsuGLoizLaYGopsj .node ellipse,#mermaid-svg-tsuGLoizLaYGopsj .node polygon,#mermaid-svg-tsuGLoizLaYGopsj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tsuGLoizLaYGopsj .node .label{text-align:center;}#mermaid-svg-tsuGLoizLaYGopsj .node.clickable{cursor:pointer;}#mermaid-svg-tsuGLoizLaYGopsj .arrowheadPath{fill:#333333;}#mermaid-svg-tsuGLoizLaYGopsj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tsuGLoizLaYGopsj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tsuGLoizLaYGopsj .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-tsuGLoizLaYGopsj .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-tsuGLoizLaYGopsj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tsuGLoizLaYGopsj .cluster text{fill:#333;}#mermaid-svg-tsuGLoizLaYGopsj .cluster span{color:#333;}#mermaid-svg-tsuGLoizLaYGopsj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tsuGLoizLaYGopsj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Application
BSP
Support

1, 添加前面的pll_config文件

把前面写的《KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记2——设置PLL》中创建的pll_config.h和pll_config.s复制到BSP里面,并在“品”里面添加到BSP包里。但是修改有关的数据,将主频调成144MHz。这样后面实现115200的波特率就数值上比较好做。

2,创建irqn_vector.s

这个就是把stm32f407xx.h文件中的所有的中断向量连同中断号一起以汇编文件的方式定义一下。

;    irqn_vector.s
;   Author:超级喵窝窝
;WWDG_IRQn                   equ        0     ; Window WatchDog Interrupt
PVD_IRQn                    equ     1     ; PVD through EXTI Line detection Interrupt
TAMP_STAMP_IRQn             equ     2     ; Tamper and TimeStamp interrupts through the EXTI line
RTC_WKUP_IRQn               equ     3     ; RTC Wakeup interrupt through the EXTI line
FLASH_IRQn                  equ     4     ; FLASH global Interrupt
RCC_IRQn                    equ     5     ; RCC global Interrupt
EXTI0_IRQn                  equ     6     ; EXTI Line0 Interrupt
EXTI1_IRQn                  equ     7     ; EXTI Line1 Interrupt
EXTI2_IRQn                  equ     8     ; EXTI Line2 Interrupt
EXTI3_IRQn                  equ     9     ; EXTI Line3 Interrupt
EXTI4_IRQn                  equ     10    ; EXTI Line4 Interrupt
DMA1_Stream0_IRQn           equ     11    ; DMA1 Stream 0 global Interrupt
DMA1_Stream1_IRQn           equ     12    ; DMA1 Stream 1 global Interrupt
DMA1_Stream2_IRQn           equ     13    ; DMA1 Stream 2 global Interrupt
DMA1_Stream3_IRQn           equ     14    ; DMA1 Stream 3 global Interrupt
DMA1_Stream4_IRQn           equ     15    ; DMA1 Stream 4 global Interrupt
DMA1_Stream5_IRQn           equ     16    ; DMA1 Stream 5 global Interrupt
DMA1_Stream6_IRQn           equ     17    ; DMA1 Stream 6 global Interrupt
ADC_IRQn                    equ     18    ; ADC1, ADC2 and ADC3 global Interrupts
CAN1_TX_IRQn                equ     19    ; CAN1 TX Interrupt
CAN1_RX0_IRQn               equ     20    ; CAN1 RX0 Interrupt
CAN1_RX1_IRQn               equ     21    ; CAN1 RX1 Interrupt
CAN1_SCE_IRQn               equ     22    ; CAN1 SCE Interrupt
EXTI9_5_IRQn                equ     23    ; External Line[9:5] Interrupts
TIM1_BRK_TIM9_IRQn          equ     24    ; TIM1 Break interrupt and TIM9 global interrupt
TIM1_UP_TIM10_IRQn          equ     25    ; TIM1 Update Interrupt and TIM10 global interrupt
TIM1_TRG_COM_TIM11_IRQn     equ     26    ; TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt
TIM1_CC_IRQn                equ     27    ; TIM1 Capture Compare Interrupt
TIM2_IRQn                   equ     28    ; TIM2 global Interrupt
TIM3_IRQn                   equ     29    ; TIM3 global Interrupt
TIM4_IRQn                   equ     30    ; TIM4 global Interrupt
I2C1_EV_IRQn                equ     31    ; I2C1 Event Interrupt
I2C1_ER_IRQn                equ     32    ; I2C1 Error Interrupt
I2C2_EV_IRQn                equ     33    ; I2C2 Event Interrupt
I2C2_ER_IRQn                equ     34    ; I2C2 Error Interrupt
SPI1_IRQn                   equ     35    ; SPI1 global Interrupt
SPI2_IRQn                   equ     36    ; SPI2 global Interrupt
USART1_IRQn                 equ     37    ; USART1 global Interrupt
USART2_IRQn                 equ     38    ; USART2 global Interrupt
USART3_IRQn                 equ     39    ; USART3 global Interrupt
EXTI15_10_IRQn              equ     40    ; External Line[15:10] Interrupts
RTC_Alarm_IRQn              equ     41    ; RTC Alarm (A and B) through EXTI Line Interrupt
OTG_FS_WKUP_IRQn            equ     42    ; USB OTG FS Wakeup through EXTI line interrupt
TIM8_BRK_TIM12_IRQn         equ     43    ; TIM8 Break Interrupt and TIM12 global interrupt
TIM8_UP_TIM13_IRQn          equ     44    ; TIM8 Update Interrupt and TIM13 global interrupt
TIM8_TRG_COM_TIM14_IRQn     equ     45    ; TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt
TIM8_CC_IRQn                equ     46    ; TIM8 Capture Compare global interrupt
DMA1_Stream7_IRQn           equ     47    ; DMA1 Stream7 Interrupt
FSMC_IRQn                   equ     48    ; FSMC global Interrupt
SDIO_IRQn                   equ     49    ; SDIO global Interrupt
TIM5_IRQn                   equ     50    ; TIM5 global Interrupt
SPI3_IRQn                   equ     51    ; SPI3 global Interrupt
UART4_IRQn                  equ     52    ; UART4 global Interrupt
UART5_IRQn                  equ     53    ; UART5 global Interrupt
TIM6_DAC_IRQn               equ     54    ; TIM6 global and DAC1&2 underrun error  interrupts
TIM7_IRQn                   equ     55    ; TIM7 global interrupt
DMA2_Stream0_IRQn           equ     56    ; DMA2 Stream 0 global Interrupt
DMA2_Stream1_IRQn           equ     57    ; DMA2 Stream 1 global Interrupt
DMA2_Stream2_IRQn           equ     58    ; DMA2 Stream 2 global Interrupt
DMA2_Stream3_IRQn           equ     59    ; DMA2 Stream 3 global Interrupt
DMA2_Stream4_IRQn           equ     60    ; DMA2 Stream 4 global Interrupt
ETH_IRQn                    equ     61    ; Ethernet global Interrupt
ETH_WKUP_IRQn               equ     62    ; Ethernet Wakeup through EXTI line Interrupt
CAN2_TX_IRQn                equ     63    ; CAN2 TX Interrupt
CAN2_RX0_IRQn               equ     64    ; CAN2 RX0 Interrupt
CAN2_RX1_IRQn               equ     65    ; CAN2 RX1 Interrupt
CAN2_SCE_IRQn               equ     66    ; CAN2 SCE Interrupt
OTG_FS_IRQn                 equ     67    ; USB OTG FS global Interrupt
DMA2_Stream5_IRQn           equ     68    ; DMA2 Stream 5 global interrupt
DMA2_Stream6_IRQn           equ     69    ; DMA2 Stream 6 global interrupt
DMA2_Stream7_IRQn           equ     70    ; DMA2 Stream 7 global interrupt
USART6_IRQn                 equ     71    ; USART6 global interrupt
I2C3_EV_IRQn                equ     72    ; I2C3 event interrupt
I2C3_ER_IRQn                equ     73    ; I2C3 error interrupt
OTG_HS_EP1_OUT_IRQn         equ     74    ; USB OTG HS End Point 1 Out global interrupt
OTG_HS_EP1_IN_IRQn          equ     75    ; USB OTG HS End Point 1 In global interrupt
OTG_HS_WKUP_IRQn            equ     76    ; USB OTG HS Wakeup through EXTI interrupt
OTG_HS_IRQn                 equ     77    ; USB OTG HS global interrupt
DCMI_IRQn                   equ     78    ; DCMI global interrupt
RNG_IRQn                    equ     80    ; RNG global Interrupt
FPU_IRQn                    equ     81    ; FPU global interrupt end

3,将常用的寄存器和外设地址命名

其实主要是因为STM32没有很合适的,类似stm32f407xx.h那样的头文件可以帮你把所有外设都定义好。不要紧,我们手动把会用到的外设按照自己的使用偏好定义了就好。

;    registers.s
;   Author: 超级喵窝窝
;   This file is used to define the registers used in this project.; General Purpose Registers
callee_regs                         rlist   {r4-r12,lr};SCB
SCB_BaseAddr                        equ     0xE000ED00
SCB_CPUID                           equ     0xE000ED00
SCB_VTOR                            equ     0xE000ED08;NVIC
NVIC_BaseAddr                       equ     0xE000E100
NVIC_ISER                           equ     0xE000E100
NVIC_ICER                           equ     0xE000E180
NVIC_ISPR                           equ     0xE000E200
NVIC_ICPR                           equ     0xE000E280
NVIC_IABR                           equ     0xE000E300
NVIC_IPR                            equ     0xE000E400; The Software trigger interrupt register is only available in STM32?
NVIC_STIR                           equ     0xE000EF00;RCC
RCC_BaseAddr                        equ     0x40023800 ;
RCC_CR                              equ     0x00
RCC_CR_HSEON                        equ     0x10000
RCC_CR_HSERDY                       equ     0x20000
RCC_CR_PLLON                        equ     0x1000000
RCC_CR_PLLRDY                       equ     0x2000000RCC_PLLCFGR                        equ     0x04
RCC_PLLCFGR_PLLSRC_HSE              equ     0x400000RCC_CFGR                            equ     0x08
RCC_CFGR_PPRE1_DIV2                 equ     0x800
RCC_CFGR_PPRE2_DIV2                 equ     0x8000
RCC_CFGR_SW_PLL                     equ     0x02RCC_AHB1ENR                         equ     0x30
RCC_APB2ENR                         equ     0x44RCC_ABP2ENR_SPI2                    equ     0x01:ROL:12
RCC_APB2ENR_USART1EN                equ     0x01:ROL:4
RCC_AHB1ENR_GPIOAEN                 equ     0x01:ROL:0
RCC_AHB1ENR_GPIOBEN                 equ     0x01:ROL:1
RCC_AHB1ENR_GPIOCEN                 equ     0x01:ROL:2
;Flash
FLASH_BaseAddr                      equ     0x40023C00FLASH_ACR                         equ     0x00
FLASH_ACR_PRFTEN                    equ     0x100
FLASH_ACR_ICEN                      equ     0x200
FLASH_ACR_DCEN                      equ     0x400
FLASH_ACR_SET                       equ     0x705;GPIO
;GPIO_BaseAddrGPIOA_BaseAddr                        equ     0x40020000
GPIOB_BaseAddr                      equ     0x40020400
GPIOC_BaseAddr                      equ     0x40020800  ;GPIO registers offset
GPIO_MODER                          equ     0x00
GPIO_OTYPER                         equ     0x04
GPIO_OSPEEDR                        equ     0x08
GPIO_PUPDR                          equ     0x0C
GPIO_BSRR                           equ     0x18
GPIO_AFRL                           equ     0x20
GPIO_AFRH                           equ     0x24
;USART
;USART_BaseAddr
USART1_BaseAddr                     equ     0x40011000
USART2_BaseAddr                     equ     0x40004400;USART registers offset
USART_SR                            equ     0x00
USART_DR                            equ     0x04
USART_BRR                           equ     0x08
USART_CR1                           equ     0x0C
USART_CR2                           equ     0x10
USART_CR3                           equ     0x14
USART_GTPR                          equ     0x18USART_SR_TC                         equ     1:ROL:6
USART_SR_RXNE                       equ     1:ROL:5USART_CR1_TE                     equ     1:ROL:3
USART_CR1_RE                        equ     1:ROL:2
USART_CR1_UE                        equ     1:ROL:13
USART_CR1_RXNEIE                    equ     1:ROL:5end

这里,有关的汇编命令,包括rlist、:ROL:(这里有人也说是伪指令。不知道该叫什么。但是手册里面英文名字叫Directives)参考文件《ARM Developer Suite Assembler Guide》。

例如:rlist是定义一个寄存器列表,:rol:是ARM汇编器的运算符。注意它们不是ARM指令或者是Thumb指令,是汇编器在汇编代码的时候运行的。跟C编译器的宏定义类似。

4,创建usb_uart.h和usb_uart.s

4.1, usb_uart.h的创建与封装

首先创建usb_uart.h文件,完成C语言的封装。

#ifndef _USB_UART_H_
#define _USB_UART_H_#include "stdint.h"typedef struct{void (*init)(void);void (*send_ch)(uint8_t);uint8_t (*read_ch)(void);
}USB_UART_Def;
extern USB_UART_Def usb_uart;#endif

4.2, usb_uart.s的创建与封装

就这块开发板而言,根据它的说明文档,使用的是uart1,并且波特率必须是115200。

4.2.1, 定义配置宏

那么创建usb_uart.s。首先定义本文内要用的宏定义。这里通过配置的Manual Configuration里面的宏,就可以完成设置。设置包括三方面:

  1. 时钟启动,启动gpio和要用的USART。
  2. Pin脚设置,设置TX和RX的pin脚,主要是MODER和AFIO。有关AFIO的值要去查Datasheet。
  3. 中断设置,主要设置优先级。由于usart的中断是由NVIC直接控制。所以不需要对SYSCFG进行操作。
  4. UART设置,主要包括波特率、使能、停止位和中断等。
;------------------------------------------
;   usb_uart.s
;   Author: 超级喵窝窝
;   Description: UART1 is used.
;                       TX: PA10
;                       RX: PA9
;               Baud rate: 115200Hz
;-------------------------------------------get registers.sget irqn_vector.s;   Manual Configurations
GPIO_PORT                   equ     GPIOA_BaseAddr          ;   Tell the Hardware engineer to forget UART5, Coz it uses PC12 and PD2.
USART_PORT                  equ     USART1_BaseAddr
USART_IRQn                  equ     USART1_IRQn
TX_Pin                      equ     0x0A                    ;   PA10 is Port A Pin 10
RX_Pin                      equ     0x09                    ;   PA9 is Port A Pin 9
GPIO_PORT_CLOCK_BUSEN       equ     RCC_AHB1ENR
GPIO_PORT_CLOCK_BIT         equ     RCC_AHB1ENR_GPIOAEN
USART_CLOCK_BUSEN           equ     RCC_APB2ENR
USART_CLOCK_BIT             equ     RCC_APB2ENR_USART1EN
RX_PIN_AF                   equ     0x07                    ;   Check this value with the datasheet.
TX_PIN_AF                   equ     0x07                    ;   Check this value with the datasheet.
NVIC_USART_PRIO_VAL         equ     1                       ;   Set the Priority of USART_IRQn to 1
buffer_size                 equ     100

完成以上的设置以后,后面有关的设置利用汇编器进行计算。这里面有几点注意:

  1. equspacern都前面不能有空格。
  2. IF...ELSE...ENDIFENDAREAMACROMEND等前面必须有空格。
  3. 汇编助记符、ARM寄存器名称和汇编器Directives不区分大小写,但是必须有一致性。比如可以写equEQU,但是不能写eQu
  4. ‘\’可以实现分行
  5. 本文中的所有的宏定义、重命名只在本文中生效。用EXPORT引出的例外。
;Calculated by the AssemblerTX_PIN_MODER_VAL         equ     0x02 :rol: (2 * TX_Pin)
RX_PIN_MODER_VAL            equ     0x02 :rol: (2 * RX_Pin)IF RX_Pin<0x08
GPIO_PORT_AFIO              equ     GPIO_AFRL
RX_PIN_AFIO_VAL             equ     RX_PIN_AF :rol: (4* RX_Pin )
TX_PIN_AFIO_VAL             equ     TX_PIN_AF :rol: (4* TX_Pin )ELSE
GPIO_PORT_AFIO              equ     GPIO_AFRH
RX_PIN_AFIO_VAL             equ     RX_PIN_AF :rol: ( 4 * (RX_Pin - 0x08))
TX_PIN_AFIO_VAL             equ     TX_PIN_AF :rol: ( 4 * (TX_Pin - 0x08))ENDIF; Calculate the address offsets of ISER, ICER, ICPR and IPR for USART1.
NVIC_ISER_USART1_OFFSET     equ     \NVIC_ISER - NVIC_BaseAddr + USART_IRQn / 32 * 4
NVIC_ICER_USART1_OFFSET     equ     \NVIC_ICER - NVIC_BaseAddr + USART_IRQn / 32 * 4
NVIC_ICPR_USART1_OFFSET     equ     \NVIC_ICPR - NVIC_BaseAddr + USART_IRQn / 32 * 4
NVIC_IPR_USART1_OFFSET      equ     \NVIC_IPR - NVIC_BaseAddr + USART_IRQn / 4 * 4 NVIC_USART_BIT              equ     \1 :rol: (USART_IRQn :mod: 32)
NVIC_IPR_USART_PRIO_VAL     equ     \NVIC_USART_PRIO_VAL :rol:(USART_IRQn :mod: 4 * 8 + 4); Configure the USART1; APB2:    72MHz; USARTDIV = 72MHz / ( 16 * 115200) = 39.0625; So the BSS_Mantissa = 39, Fraction = 1
USART_BSS_VAL       equ     0x271
4.2.2 Buffer FIFO的定义

接下来做一个buffer,按照FIFO的规则进行访问。这里做一个固定尺寸的线性表就可以。定义一个3个标量。

名称 用途
uart_buffer_nth_recv 将随时接收到的数据写入FIFO
uart_buffer_nth_read 读出已写入的数据
usart_buffer_nData 缓冲区内的数据数量

关于定义数据变量和数据块。

;    Apply a buffer for the Received Data. This buffer is actually a FIFO.area USB_USART_DATA_SECTION, data  uart_buffer             space       buffer_sizealign    4
uart_buffer_nth_recv    space       4
uart_buffer_nth_read    space       4
usart_buffer_nData      space       4pBuf_BaseAddr          equ         uart_buffer
pRecv                   equ         uart_buffer_nth_recv - uart_buffer
pRead                   equ         uart_buffer_nth_read - uart_buffer
nData                   equ         usart_buffer_nData - uart_buffer
  1. space划出一块内存给本文件里的函数。这个类似C语言的文内静态全局变量。
  2. 将段定义到rw段即可,则数据地址就会被定义到片上SRAM。但是如果非要定义到CCM上,需要进行链接脚本操作。
  3. 缓冲区里的数据都是字节,也只进行字节访问,所以不需要进行地址对齐。
  4. 后面的几个变量考虑用32位数据,所以必须进行地址对齐。
  5. align 4实现4字节地址对齐。
4.2.3 文内寄存器的重定义指定

经过分析,我们发现,其实本文中所有的函数中会常用的外设寄存器和其他内存地址有:

  1. RCC
  2. GPIO
  3. UART
  4. pBuf
  5. Buf_Size

考虑到在内核里有r0-r12共13个通用寄存器,我就尝试先奢侈一下。将所有的高寄存器(High Registers,r8 - r12)都用于放这种地址。当然,这里面还是占用了一个r7。不过先写写看,先不去规划寄存器。然后再定义有关的宏,用于在函数中加载地址。

; Define the registers commonly used in this file.
rRCC            rn      r12
rGPIO           rn      r11
rUART           rn      r10
rNVIC           rn      r9
rpBuf           rn      r8
rBuf_Size       rn      r7; MACROs used to load the internal registersmacroload_rRCCldr     rRCC, =RCC_BaseAddrmendmacroload_rGPIOldr      rGPIO, =GPIO_PORTmendmacroload_rUARTldr        rUART, =USART_PORTmendmacroload_rNVICldr       rNVIC, =NVIC_BaseAddrmendmacroload_rpBufldr        rpBuf, =pBuf_BaseAddrmov       rBuf_Size, #buffer_sizemend

这里有一个很容易昏头的问题。就是这些宏不能只在初始化的时候执行一次就可以的。我面对的是寄存器,不是C语言中的全局变量。所以每次进入函数的时候都可能是被改了的。

4.2.4 初始化函数void init(void)

其实这个函数格式只是给C函数看的。汇编里面只要有个名字就行了,其他的都是浮云。

这里注意,

  1. 所有的函数在创建的时候,最好都来个align 4,以保证读取地址的正常。有人说,thumb不是16位的么。其实那都是老Thumb的。现在的是Thumb-II,有很多32位的指令的。对齐一下自然是极好的。
  2. 所有的函数上来就来一句将所有的callee寄存器都压栈。很省事,如果你和我一样是个懒人,那就这样干,一了百了。
area USB_UART_CODE_SECTION, codealign 4
init        procpush    callee_regs;    Enable the clocks for GPIOA and USART1load_rRCCldr      r0, [rRCC, #GPIO_PORT_CLOCK_BUSEN]orr   r0, #GPIO_PORT_CLOCK_BITstr     r0, [rRCC, #GPIO_PORT_CLOCK_BUSEN]ldr   r0, [rRCC, #USART_CLOCK_BUSEN]orr   r0, #USART_CLOCK_BITstr     r0, [rRCC, #USART_CLOCK_BUSEN]; Configure the pinsload_rGPIOldr     r0, [rGPIO, #GPIO_MODER]orr     r0, #TX_PIN_MODER_VAL :or: RX_PIN_MODER_VALstr  r0, [rGPIO, #GPIO_MODER]ldr     r0, [rGPIO, #GPIO_PORT_AFIO]orr     r0, #RX_PIN_AFIO_VAL:OR:TX_PIN_AFIO_VALstr      r0, [rGPIO, #GPIO_PORT_AFIO]            ; Enable the receive interrupt by set the CR_RXNEIE bit     load_rUARTldr       r0, [rUART, #USART_CR1]orr      r0, #USART_CR1_UE ; Enable the interrupt of USART1orr       r0, #USART_CR1_RXNEIEorr        r0, #USART_CR1_REstr        r0, [rUART, #USART_CR1]mov      r0, #USART_BSS_VALstr   r0, [rUART, #USART_BRR]; Set the NVIC bits.load_rNVICldr        r1, [rNVIC, #NVIC_IPR_USART1_OFFSET]orr     r1, #NVIC_IPR_USART_PRIO_VALstr     r1, [rNVIC, #NVIC_IPR_USART1_OFFSET]            ldr     r1, =NVIC_USART_BITstr     r1, [rNVIC, #NVIC_ISER_USART1_OFFSET];pop   callee_regsbx       lrendp

可以看出,所有在C下面尤其是还用标准库、HAL库什么各种各样的库来的,在汇编这里其实都很简单的。使用了汇编器的Directives以后,很多汇编指令也变得可读了,而且,貌似就只是数指令数,也不见得比C要多。

4.2.5 void send_ch(uint8_t)函数的实现

这个汇编下面的macro…mend是个很强的存在。macro … mend之间是宏定义。我这里就直接指定,这个宏里面的语句必须使用r1,提醒我如果要用的话,必须保证r1可以用。r1本来就是caller寄存器,也应该由调用方保护。这个东西就跟C语言中的inline或者宏函数差不多。用汇编宏实现轮询SR_TC。

其次,对于ARM-CM4来说,第一个参数的值就是进入函数以后r0的值。

         macro
$label      wait_for_sr_tc_macro_via_r1
$label.test_sr_tc_mldr      r1, [rUART, #USART_SR]tst       r1, #USART_SR_TCbeq     $label.test_sr_tc_m         mendalign   4
send_ch     procpush    callee_regsload_rUARTldr        r2, [rUART, #USART_CR1]orr      r2, #USART_CR1_TEstr    r2, [rUART, #USART_CR1]
before_send     wait_for_sr_tc_macro_via_r1 ;   It is important to wait for the tc to be set.;  By checking this bit via DEBUG window is not possible;  to find if this bit is set or cleared.str       r0, [rUART, #USART_DR]
after_sent      wait_for_sr_tc_macro_via_r1 ;   This is the same as the previous waiting.bic        r2, #USART_CR1_TEstr    r2, [rUART, #USART_CR1]             pop     callee_regsbx       lrendp

void send_ch(uint8_t)里面核心就是把数据放进DR寄存器,但是这里要注意,放进去之前和之后要轮询一下TC标志位,这样就能保证发送数据时这个UART口是正常的。这个操作是很有必要的。因为如果靠调试器查的话,往往是感觉TXE和TC永远是同步的在置位和清零。

4.2.5 uint8_t read_ch(void)函数的实现

这个函数实现函数从缓冲区里读数据。如果是没有数据就在里面轮询着。
返回值就是r0的值。

对缓冲区的操作是

#mermaid-svg-Br7TyQsQI9YUMkPd {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .error-icon{fill:#552222;}#mermaid-svg-Br7TyQsQI9YUMkPd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Br7TyQsQI9YUMkPd .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Br7TyQsQI9YUMkPd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Br7TyQsQI9YUMkPd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Br7TyQsQI9YUMkPd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Br7TyQsQI9YUMkPd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Br7TyQsQI9YUMkPd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Br7TyQsQI9YUMkPd .marker.cross{stroke:#333333;}#mermaid-svg-Br7TyQsQI9YUMkPd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Br7TyQsQI9YUMkPd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .cluster-label text{fill:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .cluster-label span{color:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .label text,#mermaid-svg-Br7TyQsQI9YUMkPd span{fill:#333;color:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .node rect,#mermaid-svg-Br7TyQsQI9YUMkPd .node circle,#mermaid-svg-Br7TyQsQI9YUMkPd .node ellipse,#mermaid-svg-Br7TyQsQI9YUMkPd .node polygon,#mermaid-svg-Br7TyQsQI9YUMkPd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Br7TyQsQI9YUMkPd .node .label{text-align:center;}#mermaid-svg-Br7TyQsQI9YUMkPd .node.clickable{cursor:pointer;}#mermaid-svg-Br7TyQsQI9YUMkPd .arrowheadPath{fill:#333333;}#mermaid-svg-Br7TyQsQI9YUMkPd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Br7TyQsQI9YUMkPd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Br7TyQsQI9YUMkPd .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Br7TyQsQI9YUMkPd .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Br7TyQsQI9YUMkPd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Br7TyQsQI9YUMkPd .cluster text{fill:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd .cluster span{color:#333;}#mermaid-svg-Br7TyQsQI9YUMkPd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Br7TyQsQI9YUMkPd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Yes
No
Start
Whether the buffer is empty?
nData > 0
Read the data out.
Update the pRead
End

以下的代码实现上述的图。这里有一个不方便的地方,就是Thumb-II指令集里没有直接取模的指令,所以只能先做udivsdiv整除,再用mls这样的指令进行计算余数。要是RISC-V就好办多了。一条语句就给你把商和余数都获得了。

align    4
read_ch     functionpush    callee_regsload_rpBuf
wait_for_dataldr        r1, [rpBuf, #nData]cbnz r1, data_receivedb      wait_for_data
data_receivedldr        r2, [rpBuf, #pRead] ; Get the value of pRead to r2.ldrb r0, [rpBuf, r2]     ; Take the data from pBuf_BaseAddr + [pRead] to r0.sub     r1, #1str       r1, [rpBuf, #nData] ; Update the value of nData. Then r1 is free.add        r2, #1              ; Update the pReadudiv  r1, r2, rBuf_Size   ; Perform a MOD operator: pRead = Pread % buffer_sizemls       r2, r1, rBuf_Size, r2str        r2, [rpBuf, #pRead]pop      callee_regsbx       lrendfunc
4.2.5 引出结构体usb_uart

把函数们做好了以后,定义一个数据块,并引出去就可以了。还是,要注意4字节地址对齐。

align    4
usb_uart    dcd init, send_ch, read_chexport usb_uart

这样在工程中任何地方只要有C语言的地方使用extern,在汇编的地方使用externimport,如果是gnu汇编的话.global就可以使用usb_uart这个结构体了。

4.2.6 ISR void USART1_IRQHandler(void)

当然,不要忘了还有这个ISR。做这个ISR的时候要注意:

  1. 进入ISR的时候,是以特权模式进来的。所以很多特权指令可以直接使用。
  2. 这个函数实现了以后,要引出。否则不能覆盖系统弱定义的那个函数。
  3. 进入ISR的时候会发现返回地址lr的值是0xFFFF FFFD。这个是退出ISR的方法。参考《ARMv7-M Architecture Reference Manual》的B1-539和B1-540两页。

这个函数的流程是

#mermaid-svg-nMNZUPG6AcNeveSq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .error-icon{fill:#552222;}#mermaid-svg-nMNZUPG6AcNeveSq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nMNZUPG6AcNeveSq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-nMNZUPG6AcNeveSq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nMNZUPG6AcNeveSq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nMNZUPG6AcNeveSq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nMNZUPG6AcNeveSq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nMNZUPG6AcNeveSq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nMNZUPG6AcNeveSq .marker.cross{stroke:#333333;}#mermaid-svg-nMNZUPG6AcNeveSq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nMNZUPG6AcNeveSq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .cluster-label text{fill:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .cluster-label span{color:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .label text,#mermaid-svg-nMNZUPG6AcNeveSq span{fill:#333;color:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .node rect,#mermaid-svg-nMNZUPG6AcNeveSq .node circle,#mermaid-svg-nMNZUPG6AcNeveSq .node ellipse,#mermaid-svg-nMNZUPG6AcNeveSq .node polygon,#mermaid-svg-nMNZUPG6AcNeveSq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nMNZUPG6AcNeveSq .node .label{text-align:center;}#mermaid-svg-nMNZUPG6AcNeveSq .node.clickable{cursor:pointer;}#mermaid-svg-nMNZUPG6AcNeveSq .arrowheadPath{fill:#333333;}#mermaid-svg-nMNZUPG6AcNeveSq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nMNZUPG6AcNeveSq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nMNZUPG6AcNeveSq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-nMNZUPG6AcNeveSq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-nMNZUPG6AcNeveSq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nMNZUPG6AcNeveSq .cluster text{fill:#333;}#mermaid-svg-nMNZUPG6AcNeveSq .cluster span{color:#333;}#mermaid-svg-nMNZUPG6AcNeveSq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nMNZUPG6AcNeveSq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Yes
No
Start
Disable the interrupt.
Whether the buffer is full?
nData > 0
Delete the oldest data and forward the pRead.
Increase nData by 1.
Forward pRecv and write the buffer.
Enable the Interrupt.
End

下面的代码实现这个流程图的功能。

;
;   USART1_IRQHandler
;   Disable the Interrupt --> Clear the pending -->
;   do something --> Enable the Interrupt.
;   align   4
USART1_IRQHandler   procexport  USART1_IRQHandler   push    callee_regsload_rNVICload_rpBufldr      r4, =NVIC_USART_BIT    ;   r4 is the NVIC_USART_BIT            str     r4, [rNVIC, #NVIC_ICER_USART1_OFFSET]str        r4, [rNVIC, #NVIC_ICPR_USART1_OFFSET]; Insert the data to the buffer FIFO   ldr     r0, [rpBuf, #nData]     ;   r0 holds the value of nDataldr      r1, [rpBuf, #pRead]     ;   r1 holds the value of pRead         cmp     r0, rBuf_Size           ;   if the buffer is full, ittte    eq          addeq   r1, #1                  ;   Increase the address of pRead by 1 circularlyudiveq r2, r1, rBuf_Size       ;   Do a MOD operand.mlseq  r1, r2, rBuf_Size, r1   ;   otherwise just add the nData by 1addne  r0, #1;         streq   r1, [rpBuf, #pRead]     ;   Update the pRead if the buffer is full,strne    r0, [rpBuf, #nData]     ;   otherwise update the value of nData.; Read out the data from the USART1 RDR, which is Receive data register.; Store the data to the buffer.load_rUART                       ;   High registers!! Haha!ldr       r0, [rpBuf, #pRecv]ldrb r1, [rUART, #USART_DR]strb  r1, [rpBuf,r0]          ;   Store the data from USART_DR to  ;  pBuf_BaseAddr + [pRecv]; Update the pRecvadd       r0, #1                  ;   Increase the pRecv by 1.udiv    r1, r0, rBuf_Size       ;   Perform a MOD operand.mls       r0, r1, rBuf_Size, r0   ;str        r0, [rpBuf, #pRecv]     ;   Write back the pRecv.;  Re-enable the interrupt.str     r4, [rNVIC, #NVIC_ISER_USART1_OFFSET]   pop     callee_regsbx       lrendpend

关于这里面的程序设计,有3点可以说的

  1. 这个函数里面可以使用IT块对小规模的条件运算和条件执行进行规划。
  2. str的条件执行不需要在IT块里。还有其它的不确定的是否需要IT块执行的条件执行指令,参考《ARM Architecture Reference Manual Thumb-2 Supplement》.
  3. 就算是在ISR中,该保护一下寄存器还是要保护一下的。

第二步,Support的构建

这样,串口的驱动就有了。因为我只是为了支持STDIO的,所以不需要更多的函数,例如大规模数据读写什么的。暂时先不考虑DMA这些东西。

右键Support,add new items,user code template, STDOUT via USART。
右键Support,add new items,user code template, STDIN via USART。

把wizard里面的变量配置一下。其实我用不到,但是看着数值对不上让人不爽。

里面其他没用的删掉,就这样干。

/*-----------------------------------------------------------------------------* Name:    stdin_USART.c* Purpose: STDIN USART Template* Rev.:    1.0.0*-----------------------------------------------------------------------------*/#include "stdin_USART.h"
#include "usb_uart.h"
#include "cmsis_os2.h"                  // ::CMSIS:RTOS2//-------- <<< Use Configuration Wizard in Context Menu >>> --------------------#define USART_DRV_NUM           1//   <o>Baudrate
#define USART_BAUDRATE          115200#define _USART_Driver_(n)  Driver_USART##n
#define  USART_Driver_(n) _USART_Driver_(n)/**Initialize stdin\return          0 on success, or -1 on error.
*/
int stdin_init (void) { return (0);
}/**Get a character from stdin\return     The next character from the input, or -1 on read error.
*/
int stdin_getchar (void) {static uint8_t buf;buf = usb_uart.read_ch();
//  osDelay(1);return buf;
}
/*-----------------------------------------------------------------------------* Name:    stdout_USART.c* Purpose: STDOUT USART Template* Rev.:    1.0.0*-----------------------------------------------------------------------------*/
#include "usb_uart.h"
#include "stdout_USART.h"//-------- <<< Use Configuration Wizard in Context Menu >>> --------------------// <h>STDOUT USART Interface//   <o>Connect to hardware via Driver_USART# <0-255>
//   <i>Select driver control block for USART interface
#define USART_DRV_NUM           1//   <o>Baudrate
#define USART_BAUDRATE          115200#define _USART_Driver_(n)  Driver_USART##n
#define  USART_Driver_(n) _USART_Driver_(n)/**Initialize stdout\return          0 on success, or -1 on error.
*/
int stdout_init (void) {return (0);
}/**Put a character to the stdout\param[in]   ch  Character to output\return          The character written, or -1 on write error.
*/
int stdout_putchar (int ch) {usb_uart.send_ch((uint8_t)ch);return (ch);
}

第三步 测试

去主函数里做个线程测试一下就好。

/*----------------------------------------------------------------------------* CMSIS-RTOS 'main' function template*---------------------------------------------------------------------------*/#include "RTE_Components.h"
#include  CMSIS_device_header
#include "cmsis_os2.h"#include "bsp.h"
#include "support.h"
#include "stdio.h"
/*----------------------------------------------------------------------------* Application main thread*---------------------------------------------------------------------------*/
__NO_RETURN static void app_main (void *argument) {int val[20];(void)argument;// ...for (;;) {scanf("%d", val);printf("%d\r\n",val[0]);osDelay(3);}
}int main (void) {extern void test_case(void);// System Initializationpll_config();SystemCoreClockUpdate();usb_uart.init();stdout_init();stdin_init();test_case();osKernelInitialize();                 // Initialize CMSIS-RTOSosThreadNew(app_main, NULL, NULL);    // Create application main threadosKernelStart();                      // Start thread executionfor (;;) {}
}

第四步,编译、链接、下载、复位、运行,Ура!

四、调试与验证

打开串口调试助手,设置好串口号和波特率。发送几个数据看看能不能回应我。

当然,可以修改测试线程看看其他的效果。这里我就不演示了。

五、体会

我认为下位机的软件设计中,汇编还是有很多可以发挥的地方。我指的是直接使用汇编实现程序设计,而不是搞什么性能优化或特殊指令使用。没有必要在所有的地方都用汇编,在很多的末端函数,被C语言封装之内的很多函数,用汇编做往往有奇效。以下的优点在调试和程序设计中就很有体会。

  1. 很多程序设计其实用汇编没有以前人们说的那么难。
  2. 不用担心任何中断或异常,当然就包括RTOS调度打断你现在正在执行的那句语句。
  3. 所见即所得。每一句都是原子的。不存在C的一句里面不知道要走多远,影响了什么的问题。
  4. 写起来没那么慢。看起来代码量也不少,但是用C也得表达出同样的含义,也得不少语句。熟练了汇编写起来其实挺快的。
  5. 进入函数就有13个32位的通用寄存器任你使用,函数内的甚至都不用使用SP指针定义新的变量。
  6. 跟弱类型语言,或者说高级语言一样。没有严格的变量数据类型。你说是啥就是啥。

六、参考文件

  1. ARM Developer Suite Assembler Guide
  2. Arm®v7-M Architecture Reference Manual
  3. ARM Architecture Reference Manual Thumb-2 Supplement
  4. RM0090 Reference manual STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced Arm®-based 32-bit MCUs
  5. STM32F405xx STM32F407xx Datasheet

KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记3——串口Stdio实现相关推荐

  1. KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记4——Directives

    KEIL 5.38的ARM-CM3/4 ARM汇编设计学习笔记4--Directives 一.若干重要的DIRECTIVE介绍 二.一些概念 三.Directives详细介绍 1,GET/INCLUD ...

  2. arm汇编的学习笔记,对比x86和arm(1)-从最简单的函数谈起

    最简单的函数 x86下汇编指令 ARM下汇编指令 Hello World x86中汇编指令 ARM汇编 LDM/STM指令 LDMFD 指令 ADR指令: 知识点扩展: ADR ADRL: LDR指令 ...

  3. ARM汇编指令学习笔记(一)

    (一)数据常量定义汇编指令EQU EQU用于为程序中的常量.标号等定义一个有效的字符名称,类似于C语言中的#define,当表达式为32位常量时,可指定表达式的数据类型,CODE16,CODE32,D ...

  4. arm cef3 linux 编译_【学习笔记】CEF Linux编译

    源码编译部分转载:https://bitbucket.org/chromiumembedded/cef/wiki/MasterBuildQuickStart#markdown-header-linux ...

  5. keil 生成bin找不到afx文件_【学习笔记】Keil不能正确生成.bin文件的解决办法

    前段时间我写过如何利用CW.IAR和Keil生成image文件,效果还不错,有些用户反馈挺有帮助的,毕竟待项目开发到最后是需要生成image文件用来量产烧写,我们总不至于到最后使用调试下载吧(不过还别 ...

  6. Arm技术文档全集合(含AMBA总线,Cortex-A,Contex-M,Cortex-R系列处理器,Arm体系结构,Arm服务器,Mali GPU,Keil 开发等PDF下载)

    在这里可以下载到所有Arm技术方面的文档,我们已经为大家归类好资料,方便大家学习!持续更新中,大家可点击右下角的收藏图标收藏本帖,如果大家有补充,欢迎评论~ 首发极术社区 Cortex-A系列处理器 ...

  7. [ARM] [基础][编译]ARM的浮点功能历史分类和对应的编译选项

    前言:ARM编译的时候有很多编译选项和浮点功能相关,要真正理解这些编译选项的选择,不仅仅要了解ARM的体系构建的基础知识,可能还需要了解一下ARM的历史.之后,真对这些再考虑到ARM编译选项就比较好理 ...

  8. NXP i.MX 8M Plus工业开发板硬件说明书( 四核ARM Cortex-A53 + 单核ARM Cortex-M7,主频1.6GHz)

    前  言 本文主要介绍创龙科技TLIMX8MP-EVM评估板硬件接口资源以及设计注意事项等内容. 创龙科技TLIMX8MP-EVM是一款基于NXP i.MX 8M Plus的四核ARM Cortex- ...

  9. NXP i.MX 8M Plus工业开发板硬件说明书--上册( 四核ARM Cortex-A53 + 单核ARM Cortex-M7,主频1.6GHz)

    前  言 本文档主要介绍创龙科技TLIMX8MP-EVM评估板硬件接口资源以及设计注意事项等内容. 创龙科技TLIMX8MP-EVM是一款基于NXP i.MX 8M Plus的四核ARM Cortex ...

最新文章

  1. 面了一个 32 岁的程序员,一看就是“卷”出来的
  2. python怎么读excel文件-python读写excel文件
  3. 战胜 Flash ,HTML5 还需要什么?
  4. 【PM模块】操作功能概览
  5. 三连冠!百度PARL 拿下NeurIPS 2020电网调度竞赛双赛道冠军
  6. 专接本汇编开发工具【Masm for Winodws 集成实验环境】安装细则
  7. 如何批量删除QQ浏览器指定历史记录和导出指定的历史记录
  8. leetcode746. 使用最小花费爬楼梯
  9. Didn't find class cn.jpush.android.service.DownloadProvider on path:
  10. Mybatis的Mapper代理
  11. [费用流]Bzoj P1877 晨跑
  12. [渝粤教育] 西南科技大学 病虫防治 在线考试复习资料
  13. Mint UI - 饿了么出品的开源、轻量的移动端 UI 组件库
  14. 倾斜摄影(ContextCapture)空三/三维建模-台式、便携、单机/集群硬件配置方案2020
  15. python访问陌生人qq空间_用Python登录好友QQ空间点赞
  16. VM虚拟机 .vmdk文件,拆分多个文件,合并单个文件
  17. 什么是 SRS 呢?在我们大部分的音频播放器里都内欠有这种音效。
  18. Andriod Studio 线性布局(LinearLayout)
  19. 记一次zookeeper连接慢的问题和解决方法
  20. 英雄联盟2017赛季什么时候结束?

热门文章

  1. 【虚拟机】在Windows11上下载安装VMware虚拟机以及Ubuntu(Linux)详细操作
  2. Cookiecutter通过项目模板创建项目
  3. 中山大学南方学院计算机二级,志愿青春,砥砺前行丨2020年中山大学南方学院二级志愿组织授牌仪式顺利举行...
  4. 记:USB设备的初始化、操作、实现——前导
  5. 陈可之18岁画的《历史》被中国美术馆收藏,我的18岁呢?
  6. java实现操作系统时间片轮转进程调度算法(RR算法)
  7. 计算机音乐作品,关于建立声乐作品计算机音乐伴奏曲库的一些思考
  8. Java版工人-监工模式实现
  9. Shader入门精要读书笔记11
  10. woudcloud库一点经验