[ 返回 ]
在硬件设计中,GPIO输出仅配置了6个LED。通过寄存器GPIO_ODR(地址为0x10000000)的低6比特控制。当对应比特值为1时,LED亮。
因此,直接访问寄存器的应用代码为:(例程sparrowrv_gpio01、sparrowrv_gpio02)
#define GPIO_ODR *((volatile unsigned int *)0x10000000)
GPIO_ODR = 0x1;
GPIO_ODR = GPIO_ODR + 1;
执行应用逻辑代码和硬件相关代码分离的原则后,将硬件相关代码封装为HAL层函数:(例程sparrowrv_timer02、sparrowrv_uart02)
void setLedValue(unsigned int vValue) {
GPIO_ODR = vValue;
}
unsigned int getLedValue() {
return GPIO_ODR;
}
TangPrimer20kDock开发板上有5个按钮,S4用作Core Reset,S3用作JTAG Reset。留下3个按钮S0-S2用作GPIO输入。
通过寄存器GPIO_IDR(地址为0x10000004)的低3比特读入当前按键状态,按下时为0,松开时为1。
判断哪个按键按下松开的一个实现代码为:(例程sparrowrv_gpio02)
#define GPIO_IDR *((volatile unsigned int *)0x10000004)
int buttonPushed(){
unsigned int i, button;
i = GPIO_IDR&0x07;
if (i== 0x07)
return 0;
else if ((i&0x01)== 0)
button=1;
else if ((i&0x02)== 0)
button= 2;
else if ((i&0x04)== 0)
button= 3;
else
button = 0;
//wait for button released
while ((GPIO_IDR&0x07)!= 0x07) {
}
//
return button;
}
在硬件设计中,定时器只设计了一种循环自增模式:
定时器设计了如下几个I/O寄存器:
因此,直接访问寄存器、轮询方式的应用代码为:(例程sparrowrv_timer01)
#define TIMER_CTRL *((volatile unsigned int *)0x10000080)
#define TIMER_COUNTER *((volatile unsigned int *)0x10000084)
#define TIMER_TOP *((volatile unsigned int *)0x10000088)
#define TIMER_OVERFLOW_FLAG *((volatile unsigned int *)0x1000008c)
以上声明寄存器。
TIMER_TOP = 13500000;
TIMER_CTRL = 0x01;
Core的工作时钟是13.5MHz,因此设置TIMER_TOP为13500000是延时1秒。第二句是启动定时器。
while (1){
GPIO_ODR = GPIO_ODR + 1;
while (!TIMER_OVERFLOW_FLAG) {
//wait for timeout
}
}
其中的“while (!TIMER_OVERFLOW_FLAG) ”就是等待定时器溢出。
执行应用逻辑代码和硬件相关代码分离的原则后,将硬件相关代码封装为HAL层函数:(例程sparrowrv_timer02)
void setTimerTopValue(unsigned int vValue) {
TIMER_TOP = vValue;
}
void startTimer() {
TIMER_CTRL = 0x01;
}
注:定时器中断应用在后面的小节介绍。
在硬件设计中,UART的配置只固定为一种模式:
UART设计了如下几个I/O寄存器:
因此,直接访问寄存器、轮询方式的应用代码为:(例程sparrowrv_uart01)
#define UART_TXD *((volatile unsigned int *)0x10000040)
#define UART_RXD *((volatile unsigned int *)0x10000040)
#define UART_RX_VALID *((volatile unsigned int *)0x10000044)
#define UART_TX_BUSY *((volatile unsigned int *)0x10000048)
以上声明寄存器。
UART_TXD = gValue;
上句在不判断发送状态下发送数据。
if (UART_RX_VALID){
UART_TXD = UART_RXD;
}
上句在不判断发送状态下转发收到的数据。
执行应用逻辑代码和硬件相关代码分离的原则后,将硬件相关代码封装为HAL层函数:(例程sparrowrv_uart02)
void _putchar(char character){
while (UART_TX_BUSY){
}
UART_TXD = character;
}
void uartSend(unsigned int vData) {
_putchar(vData);
}
//blocking function: return until receive byte
unsigned int uartReceive() {
while (!UART_RX_VALID){
}
return UART_RXD;
}
硬件设计中,在peripheral模块中包含了一个intctrl模块,负责外部中断屏蔽管理。其中定义了如下I/O寄存器:
定时器overflow中断请求在bit0;
UART收到数据中断请求在bit1。
在RISCV的规范中,定义了一系列的CSR寄存器用于中断管理。其中包括:
## set interrupt vector
la t0, trap_entry
csrw mtvec,t0
以上设置中断矢量表入口为trap_entry,且所有中断类型均指向同一个入口地址。
trap_entry在trap_entry.S中定义,先保护现场,然后调用trap_handler(传入mcause和mepc)执行实际的中断服务程序,然后恢复现场,执行mret中断返回。
.section .text.entry
.align 2
.global trap_entry
trap_entry:
## enable interrupt
li t0, 0x888
csrw mie,t0
以上允许外部中断。
开全局中断和开定时器中断、UART接收中断的封装函数代码:
#define PERI_EFFECT_INT_REQ *((volatile unsigned int *)0x10003fc0)
#define PERI_INT_MASK *((volatile unsigned int *)0x10003fc4)
#define PERI_PENDING_INT_REQ *((volatile unsigned int *)0x10003fc8)
#define PERI_INT_MASK_VALUE_TIMER 0x01
#define PERI_INT_MASK_VALUE_UART 0x02
void enableGlobalInt() {
__asm__ __volatile__("csrsi mstatus,0x08");
}
void enableTimerInt() {
PERI_INT_MASK |= PERI_INT_MASK_VALUE_TIMER;
}
void enableUartRxvalidInt() {
PERI_INT_MASK |= PERI_INT_MASK_VALUE_UART;
}
分发中断事件处理的代码:
//external interrupt handler
void handler_interrupt_ex() {
unsigned int request;
request = PERI_EFFECT_INT_REQ;
if (request & PERI_INT_MASK_VALUE_TIMER) {
handler_timer_overflow();
}
if (request & PERI_INT_MASK_VALUE_UART) {
handler_uart_rxvalid();
}
}
hal_basic.c中,定时器中断的处理函数,设置gTimeoutFlag标志:
unsigned int gTimeoutFlag;
__attribute__((weak)) void handler_timer_overflow() {
gTimeoutFlag = TIMER_OVERFLOW_FLAG;
}
main函数中,检查gTimeoutFlag标志触发动作:
gTimeoutFlag = 0;
setTimerTopValue(13500000);
enableTimerInt();
enableGlobalInt();
startTimer();
while(1){
if (gTimeoutFlag) {
setLedValue(getLedValue()+1);
printf("timeout...\n");
gTimeoutFlag = 0;
}
}
hal_basic.c中,UART接收中断的处理函数,读入数据,设置gUartRxDataFlag标志:
unsigned int gUartRxData;
unsigned int gUartRxDataFlag;
__attribute__((weak)) void handler_uart_rxvalid() {
gUartRxData = UART_RXD;
gUartRxDataFlag = 1;
}
main函数中,检查gUartRxDataFlag标志触发动作:
enableUartRxvalidInt();
enableGlobalInt();
while(1){
if (gUartRxDataFlag) {
setLedValue(gUartRxData);
printf("rx: %x\n", gUartRxData);
gUartRxDataFlag = 0;
}
}
I_ROM分配的地址是0x0000xxxx,RESET后执行的第一条指令从地址0x0000_0000获取。
在应用软件中对应的定义在startup.S和link.lds。
startup.S中的相关代码:
.section .init; /*声明此处段名为.init*/
.globl _start; /*声明_start是全局的*/
.type _start,@function /*声明_start是函数*/
_start:
_start在.init段。
link.lds中的相关代码:
ENTRY(_start)
声明_start为程序入口。
_iram_base_addr = 0x00000000;
_sram_base_addr = 0x20000000;
MEMORY
{
iram (rx) : ORIGIN = _iram_base_addr, LENGTH = _iram_app_size
sram (rw) : ORIGIN = _sram_base_addr, LENGTH = _cpu_sram_size
}
声明iram输出段的位置在0x00000000。
SECTIONS
{
__stack_size = DEFINED(__stack_size) ? __stack_size : _cpu_stack_size; /*堆栈区大小*/
.init :
{
KEEP (*(SORT_NONE(.init)))
} >iram AT>iram
.text :
{
*(.text.unlikely .text.unlikely.*)
*(.text.startup .text.startup.*)
*(.text .text.*)
*(.gnu.linkonce.t.*)
} >iram AT>iram
最先把.init段的内容放在iram输出段中,然后才放.text段的内容(一般代码放.text段)。
以上保证了_start函数出现在0x0000_0000的的位置。
将静态数据从I_ROM区域复制到D_RAM区。(代码来自“小麻雀处理器”仓例程)
/*加载data段,存储需要初始化的全局变量和静态变量*/
la a0, _data_lma /*程序存储器的data段起始地址 加载至 a0*/
la a1, _data /*数据存储器的data段起始地址 加载至 a1*/
la a2, _edata /*数据存储器的data段结束地址 加载至 a2*/
bgeu a1, a2, 2f /*a1大于等于a2,跳转至往下第一个2标签;否则向下执行*/
1:
lw t0, (a0) /*a0指向的地址 写入 t0*/
sw t0, (a1) /*t0的数据 写入 a1指向的地址 */
addi a0, a0, 4 /*a0+4*/
addi a1, a1, 4 /*a1+4*/
bltu a1, a2, 1b /*a1小于a2,跳转至往上第一个1标签;否则向下执行*/
/*加载data段*/
2:
/*清空bss段,存储不用初始化的全局变量和静态变量*/
la a0, __bss_start /*bss段起始地址 加载至 a0*/
la a1, _end /*bss段结束地址 加载至 a1*/
bgeu a0, a1, 2f /*a0大于等于a1,跳转至往下第一个2标签;否则向下执行*/
1:
sw zero, (a0) /*a0指向的地址 写入 0*/
addi a0, a0, 4 /*a0+4*/
bltu a0, a1, 1b /*a0小于a1,跳转至往上第一个1标签;否则向下执行*/
/*清空bss段*/
2:
其中相关参数的位置在link.lds中定义。
[ 返回 ]
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )