調通開發板的UART和GPIO
編寫makefile和linkscript
makefile:
SRCS += ch32v307.sFW_NAME ?= ch32v307CROSS_COMPILE ?= riscv64-unknown-elf-CC = $(CROSS_COMPILE)gccOD = $(CROSS_COMPILE)objdumpOC = $(CROSS_COMPILE)objcopySZ = $(CROSS_COMPILE)sizeLINK_SCRIPT ?= link.ldCFLAGS += \-march=rv32imac_zicsr -mabi=ilp32 \-mno-relax -nostdlib \-x assembler-with-cpp -ggdb \-T $(LINK_SCRIPT)all: $(FW_NAME).elf $(FW_NAME).bin $(FW_NAME).hex $(FW_NAME).dis$(SZ) $(FW_NAME).elfclean:rm -f *.out $(FW_NAME).dis $(FW_NAME).elf $(FW_NAME).bin $(FW_NAME).hex$(FW_NAME).hex:$(OC) -O ihex $(FW_NAME).elf $(FW_NAME).hex$(FW_NAME).bin:$(OC) -O binary $(FW_NAME).elf $(FW_NAME).bin$(FW_NAME).elf:$(CC) $(CFLAGS) $(SRCS) -o $(FW_NAME).elf$(FW_NAME).dis: $(FW_NAME).elf$(OD) -d -s $(FW_NAME).elf > $(FW_NAME).dis
這個makefile編譯完成後會生成 bin hex 文件,分別用於不同的燒錄軟件:
bin:
- wchisp (https://github.com/ch32-rs/wchisp) 使用Rust編寫的開源燒錄軟件,可以從USB端口燒錄
hex:
- WCH-LinkUtily (https://www.wch.cn/downloads/WCH-LinkUtility_ZIP.html) 沁恆官方的燒錄軟件,通過WCHLink鏈接開發板的SWD端口進行燒錄
- wchisptools (https://www.wch.cn/download/WCHISPTool_Setup_exe.html) 沁恆官方的燒錄軟件,通過電腦鏈接開發板的USB口進行燒錄
linkscript:
注意: 這裏的ROM, RAM的大小是CH32V307配置過的,通過WCHLinkUtilty配置
如果你的下載軟件無法配置的話最好把這裏設置爲最小:
ROM: 192K RAM: 32K
ENTRY(_start)MEMORY{ROM (rx) : ORIGIN = 0x00000000, LENGTH = 192KRAM (rwx): ORIGIN = 0x20000000, LENGTH = 128K}SECTIONS{.text :{*(.text)*(.text*). = ALIGN(4);*(.rodata)*(.rodata*). = ALIGN(4);} >ROM AT>ROM.bss (NOLOAD) :{. = ALIGN(4);*(.bss). = ALIGN(4);} >RAM AT>ROM}
將這個文件保存爲 link.ld
調通GPIO
使用匯編語言點亮一個LED比UART要容易一點
大多數人遇到最新的開發板第一件要做的事情就是點燈了
CH32V307操縱GPIO的流程:
- 在RCC控制器裏面使能對應GPIO控制器的時鐘
- 在GPIO控制器裏面配置引腳的模式爲推輓輸出
- 在GPIO控制器裏面拉高拉低引腳
點亮 PB4 上的 LED
我這裏用的開發板是塬地的一款開發板,它在引腳PB4上鏈接了一個LED
代碼示例:
.global _start_start:.option norvc;j reset.option rvc;reset:.equ RCC_BASE, 0x40021000.equ RCC_APB2PCENR, 0x18.equ RCC_IOPB_EN, (1 << 3)# 使能GPIOB的时钟li t0, RCC_BASElw t1, RCC_APB2PCENR(t0)li t2, RCC_IOPB_ENor t1, t1, t2sw t1, RCC_APB2PCENR(t0).equ GPIOB_BASE, 0x40010C00.equ GPIO_CFGLR, 0x00.equ GPIO_OUTDR, 0x0C.equ GPIO_PB4CFG_MASK, (0xF << 16).equ GPIO_PB4CFG_PPOUT_2Mhz, (0x2 << 16)# 将PB4配置为推挽输出模式li t0, GPIOB_BASElw t1, GPIO_CFGLR(t0)li t2, GPIO_PB4CFG_MASKxori t2, t2, -1and t1, t1, t2li t2, GPIO_PB4CFG_PPOUT_2Mhzor t1, t1, t2sw t2, GPIO_CFGLR(t0)# PB4输出高电平lw t1, GPIO_OUTDR(t0)ori t1, t1, (1 << 4)sw t1, GPIO_OUTDR(t0)1:j 1b
將這些代碼寫入 ch32v307.s 文件然後保存,使用 make 編譯,然後燒錄程序到電路板
接下來就可以看到PB4上的LED被點亮了
如果你的板子沒有LED,也可以用示波器,邏輯分析儀或者萬用表測量一下PB4引腳
點亮 PA15 上的 LED
在塬地的開發板上有第二顆可以主控控制的LED,鏈接到PA15引腳上
示例看到這裏,考驗你的時候到了
你可以試着自己閱讀寄存器手冊來點亮這顆LED,一定要有耐心。
不過點不亮的話我也會提供參考代碼:
.global _start_start:.option norvc;j reset.option rvc;reset:.equ RCC_BASE, 0x40021000.equ RCC_APB2PCENR, 0x18.equ RCC_IOPB_EN, (1 << 3).equ RCC_IOPA_EN, (1 << 2)# 使能GPIOB GPIOA的时钟li t0, RCC_BASElw t1, RCC_APB2PCENR(t0)li t2, RCC_IOPB_EN | RCC_IOPA_ENor t1, t1, t2sw t1, RCC_APB2PCENR(t0).equ GPIOB_BASE, 0x40010C00.equ GPIOA_BASE, 0x40010800.equ GPIO_CFGLR, 0x00.equ GPIO_CFGHR, 0x04.equ GPIO_OUTDR, 0x0C.equ GPIO_PB4CFG_MASK, (0xF << 16).equ GPIO_PB4CFG_PPOUT_2Mhz, (0x2 << 16).equ GPIO_PA15CFG_MASK, (0xF << 28).equ GPIO_PA15CFG_PPOUT_2Mhz, (0x2 << 28)# 将PB4配置为推挽输出模式li t0, GPIOB_BASElw t1, GPIO_CFGLR(t0)li t2, GPIO_PB4CFG_MASKxori t2, t2, -1and t1, t1, t2li t2, GPIO_PB4CFG_PPOUT_2Mhzor t1, t1, t2sw t2, GPIO_CFGLR(t0)# 将PA15配置为推挽输出模式li t0, GPIOA_BASElw t1, GPIO_CFGHR(t0)li t2, GPIO_PA15CFG_MASKxori t2, t2, -1and t1, t1, t2li t2, GPIO_PA15CFG_PPOUT_2Mhzor t1, t1, t2sw t2, GPIO_CFGHR(t0)# PB4输出高电平li t0, GPIOB_BASElw t1, GPIO_OUTDR(t0)ori t1, t1, (1 << 4)sw t1, GPIO_OUTDR(t0)# PA15输出高电平li t0, GPIOA_BASElw t1, GPIO_OUTDR(t0)li t2, (1 << 15)or t1, t1, t2sw t1, GPIO_OUTDR(t0)1:j 1b
調通 UART1 TX
CH32V307使用UART TX的流程:
- 在RCC控制器裏面使能對應UART控制器的時鐘
- 在GPIO控制器裏面配置引腳的模式爲推輓復用輸出
- 在UART控制器裏面配置波特率,使能UART控制器,配置模式,使能TX
- 將數據填入UART控制器,然後通過TX引腳發送出去
配置PA9爲UART1 TX,然後配置UART控制器,然後使用115200波特率輸出一大堆 0x55,代碼在這裏:
.global _start_start:.option norvc;j reset.option rvc;reset:.equ RCC_BASE, 0x40021000.equ RCC_APB2PCENR, 0x18.equ RCC_IOPB_EN, (1 << 3).equ RCC_IOPA_EN, (1 << 2).equ RCC_UART1_EN, (1 << 14)# 使能时钟:# GPIOA GPIOB# UART1li t0, RCC_BASElw t1, RCC_APB2PCENR(t0)li t2, RCC_IOPB_EN | RCC_IOPA_EN | RCC_UART1_ENor t1, t1, t2sw t1, RCC_APB2PCENR(t0).equ GPIOB_BASE, 0x40010C00.equ GPIOA_BASE, 0x40010800.equ GPIO_CFGLR, 0x00.equ GPIO_CFGHR, 0x04.equ GPIO_OUTDR, 0x0C.equ GPIO_PB4CFG_MASK, (0xF << 16).equ GPIO_PB4CFG_PPOUT_2Mhz, (0x2 << 16).equ GPIO_PA15CFG_MASK, (0xF << 28).equ GPIO_PA15CFG_PPOUT_2Mhz, (0x2 << 28).equ GPIO_PA9CFG_MASK, (0xF << 4).equ GPIO_PA9CFG_MUPPOUT_50Mhz, (0xB << 4)# 将PB4配置为推挽输出模式li t0, GPIOB_BASElw t1, GPIO_CFGLR(t0)li t2, GPIO_PB4CFG_MASKxori t2, t2, -1and t1, t1, t2li t2, GPIO_PB4CFG_PPOUT_2Mhzor t1, t1, t2sw t2, GPIO_CFGLR(t0)# 将PA15配置为推挽输出模式# 将PA9配置为复用推挽输出模式li t0, GPIOA_BASElw t1, GPIO_CFGHR(t0)li t2, GPIO_PA15CFG_MASK | GPIO_PA9CFG_MASKxori t2, t2, -1and t1, t1, t2li t2, GPIO_PA15CFG_PPOUT_2Mhz | GPIO_PA9CFG_MUPPOUT_50Mhzor t1, t1, t2sw t2, GPIO_CFGHR(t0)# PB4输出高电平li t0, GPIOB_BASElw t1, GPIO_OUTDR(t0)ori t1, t1, (1 << 4)sw t1, GPIO_OUTDR(t0)# PA15输出高电平li t0, GPIOA_BASElw t1, GPIO_OUTDR(t0)li t2, (1 << 15)or t1, t1, t2sw t1, GPIO_OUTDR(t0).equ UART1_BASE, 0x40013800.equ UART_DATAR, 0x04.equ UART_BRR, 0x08# 从寄存器手册内容和时钟树可以得到:# UART1的时钟源为PCLK2,即APB2提供的# PCLK2的时钟源是HCLK# HCLK的时钟源是SYSCLK# SYSCLK默认使用HSI,SYSCLK=8Mhz# SYSCLK根据HPRE分频得到HCLK,默认没有分频,HCLK=8Mhz# HCLK根据PPRE2分频得到PCLK2,默认没有分频,PCLK2=8Mhz# 波特率计算公式:# UART1这里的FCLK是PCLK2# 波特率 = FCLK / (16 * UARTDIV)# 那么:# (8 * 1000 * 1000) / (16 * 4.34) = 115207# UARTDIV=4.34# UARTDIV的计算公式:# UARTDIV=DIV_M+(DIV_F/16)# 那么:# 4 + (5 / 16) = 4.3125# DIV_M=4# DIV_F=5.equ BAUD_115200, ((4 << 4) | (5 << 0))li t0, UART1_BASE# 配置UART1波特率为115200li t1, BAUD_115200sw t1, UART_BRR(t0)# 使能UART控制器和TX功能.equ UART_CTLR1, 0x0C.equ UART_UE, (1 << 13).equ UART_TE, (1 << 3)li t1, UART_UE | UART_TEsw t1, UART_CTLR1(t0)# UART1 PA9 TX 不断发送0x55li t0, UART1_BASEli t1, 0x551:sw t1, UART_DATAR(t0)j 1b
鏈接電路板的PA9到USB轉串口的RX腳,上電燒錄這段代碼,復位電路板,
然後配置USB轉串口的波特率爲115200,打開串口軟件,會顯示接收到一大堆UUUU
到這裏串口TX的程序仍然沒有結束,我們還需要處理一個問題:
按UART控制器的速率傳輸數據,即我們需要UART控制器傳輸完成後再往發送寄存器填下一個字節的數據。
我們需要改一下發送的代碼:
# UART1 PA9 TX 不断发送 0x55 0x75.equ UART_STATR, 0x00.equ UART_TC, (1 << 6)li t0, UART1_BASEli t2, 0x552:1:lw t1, UART_STATR(t0)andi t1, t1, UART_TCbeqz t1, 1bsw t2, UART_DATAR(t0)xori t2, t2, (1 << 5)j 2b
這樣,你的串口軟件應該會收到一大堆UuUuUuUuUuUuUu了