IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 无剑100SOC(wujian100)挂UART外设之④修改SDK -> 正文阅读

[嵌入式]无剑100SOC(wujian100)挂UART外设之④修改SDK

一、流程

printf函数, 是如何调用串口的呢?整理出这个流程,可以帮助我们修改SDK。

首先,我们知道,C语言中,printf函数循环调用fputc函数。在sdk工程中搜索fputc函数,我们可以得到如下内容。

int fputc(int ch, FILE *stream)
{
    (void)stream;

    if (console_handle == NULL) {
        return -1;
    }

    if (ch == '\n') {
        csi_usart_putchar(console_handle, '\r');
    }

    csi_usart_putchar(console_handle, ch);

    return 0;
}

csi_usart_putchar函数

int32_t csi_usart_putchar(usart_handle_t handle, uint8_t ch)
{
    return drv_usi_usart_putchar(handle, ch);
}

drv_usi_usart_putchar函数

int32_t drv_usi_usart_putchar(usart_handle_t handle, uint8_t ch)
{
    wj_usi_usart_priv_t *usart_priv = handle;
    wj_usi_reg_t *addr = (wj_usi_reg_t *)(usart_priv->base);
    //addr->USI_EN = 0xb;
    //addr->USI_EN = 0xf;
    addr->USI_TX_RX_FIFO = ch;

    while (!(addr->USI_FIFO_STA & 0x1));

    return 0;
}

可以看出,这三个函数说明了printf是如何输出到串口的。printf函数循环调用fputc函数,fputc函数调用了csi_usart_putchar函数,csi_usart_putchar实际为drv_usi_usart_putchar函数,drv_usi_usart_putchar函数将USI_TX_RX_FIFO的数据取出送出去。这样,printf函数的内容,被UART串口送出,被PC端的串口接收,显示出来。流程图如下。

二、思路

看drv_usi_usart_putchar函数的代码,数据的基地址为addr,而addr是从(wj_usi_reg_t *)(usart_priv->base)来的。那我们在SDK工程中搜索wj_usi_reg_t和usart_priv。结果如下

typedef struct {
    __IOM uint32_t USI_EN;                 /* Offset 0x000(R/W) */
    __IOM uint32_t USI_MODE_SEL;           /* Offset 0x004(R/W) */
    __IOM uint32_t USI_TX_RX_FIFO;         /* Offset 0x008(R/W) */
    __IOM uint32_t USI_FIFO_STA;           /* Offset 0x00c(R/W) */
    __IOM uint32_t USI_CLK_DIV0;           /* Offset 0x010(R/W) */
    __IOM uint32_t USI_CLK_DIV1;           /* Offset 0x014(R/W) */
    __IOM uint32_t USI_UART_CTRL;          /* Offset 0x018(R/W) */ 
    __IOM uint32_t USI_UART_STA;           /* Offset 0x01c(R/W) */
    __IOM uint32_t USI_I2C_MODE;           /* Offset 0x020(R/W) */
    __IOM uint32_t USI_I2C_ADDR;           /* Offset 0x024(R/W) */
    __IOM uint32_t USI_I2CM_CTRL;          /* Offset 0x028(R/W) */
    __IOM uint32_t USI_I2CM_CODE;          /* Offset 0x02c(R/W) */
    __IOM uint32_t USI_I2CS_CTRL;          /* Offset 0x030(R/W) */
    __IOM uint32_t USI_I2C_FM_DIV;         /* Offset 0x034(R/W) */
    __IOM uint32_t USI_I2C_HOLD;           /* Offset 0x038(R/W) */
    __IOM uint32_t USI_I2C_STA;            /* Offset 0x03c(R/W) */
    __IOM uint32_t USI_SPI_MODE;           /* Offset 0x040(R/W) */
    __IOM uint32_t USI_SPI_CTRL;           /* Offset 0x044(R/W) */
    __IOM uint32_t USI_SPI_STA;            /* Offset 0x048(R/W) */
    __IOM uint32_t USI_INTR_CTRL;          /* Offset 0x04c(R/W) */
    __IOM uint32_t USI_INTR_EN;            /* Offset 0x050(R/W) */
    __IOM uint32_t USI_INTR_STA;           /* Offset 0x054(R/W) */
    __IOM uint32_t USI_RAW_INTR_STA;       /* Offset 0x058(R/W) */
    __IOM uint32_t USI_INTR_UNMASK;        /* Offset 0x05c(R/W) */
    __IOM uint32_t USI_INTR_CLR;           /* Offset 0x060(R/W) */
    __IOM uint32_t USI_DMA_CTRL;           /* Offset 0x064(R/W) */
    __IOM uint32_t USI_DMA_THRESHOLD;      /* Offset 0x068(R/W) */
} wj_usi_reg_t;
wj_usi_usart_priv_t *usart_priv = &usi_usart_instance[idx];

wj_usi_usart_priv_t为一个结构体,usart_priv是一个指向它的指针,具体值为usi_usart_instance[idx],那我们继续查找usi_usart_instance和idx。usi_usart_instance结果如下,idx是传进来的参数,这个我们一会讨论。

static wj_usi_usart_priv_t usi_usart_instance[CONFIG_USI_NUM];

在工程中查找CONFIG_USI_NUM,结果如下。是3,暂时看不出来从哪里计算出来的3。看来走到了头。那我们走usi_usart_instance的路走不通,我们去走idx的路。

#define CONFIG_USI_NUM 3

wj_usi_usart_priv_t *usart_priv = &usi_usart_instance[idx];这句话,在工程中,出现了两次,都在wj_usi_usart.c中。我们去找到它俩,可以看出,它俩分别是两个函数中的一个话, 这两个函数是drv_usi_usart_initialize和wj_usi_usart_irqhandler。看名字,第一个是USI的UART初始化函数,第二个是IRQ的中断安装。我们分别去找这两条路。

IRQ路,查找该函数,找到如下内容。根据USI_MODE_SEL选择UART或者I2C或者SPI,这个和我们在前边学习到的,USI0中包含有这三种通信方式相符合。继续查找wj_usi_irqhandler函数

void wj_usi_irqhandler(int32_t idx)
{
    wj_usi_priv_t *usi_priv = &usi_instance[idx];
    wj_usi_reg_t *addr = (wj_usi_reg_t *)(usi_priv->base);

    switch (addr->USI_MODE_SEL & 0x3) {
        case USI_MODE_UART:
#ifndef  CONFIG_CHIP_PANGU
            wj_usi_usart_irqhandler(idx);
#endif
            break;

        case USI_MODE_I2C:
            wj_usi_i2c_irqhandler(idx);
            break;

        case USI_MODE_SPI:
            wj_usi_spi_irqhandler(idx);
            break;

        default:
            return;
    }
}

查到结果如下,同时观察上下,三个USI的中断安装实现,同时注意其中的wj_usi_irqhanlder的参数,0、1、2。看起来我们走到了终点。那换一条路。查找drv_usi_usart_initialize函数。

ATTRIBUTE_ISR void USI0_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(0);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI1_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(1);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI2_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(2);
    CSI_INTRPT_EXIT();
}

查找结果如下,被csi_usart_initialize函数调用。继续。

usart_handle_t csi_usart_initialize(int32_t idx, usart_event_cb_t cb_event)
{
    return drv_usi_usart_initialize(idx, cb_event);
}

在board_init.c中,如下。看名字是一个初始化函数。

console_handle = csi_usart_initialize(CONSOLE_IDX, NULL);

看board_init.c文件中的函数board_init(),其中,和UART相关的有两个,第一个是一个初始化函数,第二个是一个配置函数。

void board_init(void)
{
    int32_t ret = 0;//32位有符号数
    /* init the console*/
    clock_timer_init();
    clock_timer_start();

    console_handle = csi_usart_initialize(CONSOLE_IDX, NULL);//csi_usart_initialize --> return drv_usi_usart_initialize(CONSOLE_IDX, NULL);
                                                             //CONSOLE_IDX = 0  in the file "pin.h"
    /* config the UART */
    ret = csi_usart_config(console_handle, 115200, USART_MODE_ASYNCHRONOUS, USART_PARITY_NONE, USART_STOP_BITS_1, USART_DATA_BITS_8);
                    //return drv_usi_usart_config(console_handle, 115200, USART_MODE_ASYNCHRONOUS, USART_PARITY_NONE, USART_STOP_BITS_1, USART_DATA_BITS_8);
    if (ret < 0) {
        return;
    }
}

查找board_init()函数的调用情况。在START.s,即初始化文件中,可以查到如下。

#ifndef __NO_BOARD_INIT
  120:     jal     board_init
  121  #endif

查找csi_usart_initialize中的CONSOLE_IDX,结果如下

#define CONSOLE_IDX     0

是0,结合上面出现的0、1、2,还有CONFIG_USI_NUM=3,再想到无剑100一共有3个USI模块,看起来很巧合。我们在查找一下CONFIG_USI_NUM。

const sg_usi_config[CONFIG_USI_NUM] = {
    {WJ_USI0_BASE, USI0_IRQn, USI0_IRQHandler},
    {WJ_USI1_BASE, USI1_IRQn, USI1_IRQHandler},
    {WJ_USI2_BASE, USI2_IRQn, USI2_IRQHandler},
};

在的devices.c中,如上,看起来和我们的猜测相符合,sg_usi_config是一个3行数组,每一行都是一个USI相关信息。我们分别取查找一下。

#define WJ_USI0_BASE              (0x50028000UL)
#define WJ_USI1_BASE              (0x60028000UL)
#define WJ_USI2_BASE              (0x50029000UL)
    USI0_IRQn                       =   28,     /* usi0 Interrupt */
    USI1_IRQn                       =   29,     /* usi1 Interrupt */
    USI2_IRQn                       =   30,     /* usi2 Interrupt */
ATTRIBUTE_ISR void USI0_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(0);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI1_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(1);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI2_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(2);
    CSI_INTRPT_EXIT();
}

?结合上面的信息,我们可以看到,在board_init()函数中,初始化了UART,并配置了UART的参数,而有3个USI,CONSOLE_IDX=0表明了使用的是USI0。将这一切整理为一个流程图如下。

说明各函数作用。

初始化路线,board_init调用csi_usart_initialize函数,csi_usart_initialize掉用drv_usi_usart_initialize。在drv_usi_usart_initialize中,共调用五个函数。

1)target_usi_usart_init,根据idx的值,将sg_usi_config的基地址、IRQ中断号和安装程序赋值给我们要使用的。默认情况下,idx为0,即将USI0作为要使用的USI模块。?

int32_t target_usi_usart_init(int32_t idx, uint32_t *base, uint32_t *irq, void **handler)
{
    if (idx >= CONFIG_USI_SPI_NUM) {
        return -1;
    }

    if (base != NULL) {
        *base = sg_usi_config[idx].base;
    }

    if (irq != NULL) {
        *irq = sg_usi_config[idx].irq;
    }

    if (handler != NULL) {
        *handler = sg_usi_config[idx].handler;
    }

    return idx;
}

2)drv_usi_initializ函数,给usi_priv指向的w_usi_priv_t结构体赋值

int32_t drv_usi_initialize(int32_t idx)
{
    uint32_t base = 0u;
    uint32_t irq = 0u;

    int32_t ret = target_usi_init(idx, &base, &irq);

    if (ret < 0 || ret >= CONFIG_USI_NUM) {
        return ERR_USI(DRV_ERROR_PARAMETER);
    }

    wj_usi_priv_t *usi_priv = &usi_instance[idx];
    usi_priv->base = base;
    usi_priv->irq  = irq;

    return 0;
}

3)wj_usi_set_rxfifo_th,设置FIFO

void wj_usi_set_rxfifo_th(wj_usi_reg_t *addr, uint32_t length)
{
    addr->USI_INTR_CTRL &= ~USI_INTR_CTRL_TH_MODE;
    addr->USI_INTR_CTRL &= USI_INTR_CTRL_RXFIFO_TH;

    if (length >= USI_RX_MAX_FIFO) {
        addr->USI_INTR_CTRL |= USI_INTR_CTRL_RXFIFO_TH_12 | USI_INTR_CTRL_TH_MODE;
    } else if (length >= USI_RX_MAX_FIFO - 4) {
        addr->USI_INTR_CTRL |= USI_INTR_CTRL_RXFIFO_TH_8 | USI_INTR_CTRL_TH_MODE;
    } else if (length >= 4) {
        addr->USI_INTR_CTRL |= USI_INTR_CTRL_RXFIFO_TH_4 | USI_INTR_CTRL_TH_MODE;
    } else {
        addr->USI_INTR_CTRL |= USI_INTR_CTRL_RXFIFO_TH_4;
    }
}

4)drv_irq_register,配置IRQ的寄存器

void drv_irq_register(uint32_t irq_num, void *irq_handler)
{
    g_irqvector[irq_num] = irq_handler;
}

5)drv_irq_enable,使能IRQ与否

void drv_irq_enable(uint32_t irq_num)
{
#ifdef CONFIG_SYSTEM_SECURE
    csi_vic_enable_sirq(irq_num);
#else
    csi_vic_enable_irq(irq_num);
#endif
}

配置UART串口参数路线,各个函数的功能由函数名就可以看出,再次不赘述,且与添加USI之后更改SDK无关。

三、更改

综上,我们得到了更改SDK所需要的信息。那么有两种思路可以选择。第一,将USI0的基地址,替换为dummy_top0的基地址。所谓偷梁换柱法。如下,即可

#define WJ_USI0_BASE              (0x50028000UL)

换成

#define WJ_USI0_BASE              (0x50004000UL)

第二种,讲我们挂的USI_myself,作为第四个USI,写入程序,并将初始化的USI从USI0改为USI_myself。具体需要更改的位置很多,如下。

1)soc.h中新增基地址

#define WJ_USI0_BASE              (0x50028000UL)
#define WJ_USI1_BASE              (0x60028000UL)
#define WJ_USI2_BASE              (0x50029000UL)
#define WJ_USI0_BASE_myself       (0x50004000UL)

2)soc.h中新增中断号

typedef enum IRQn {
    User_Software_IRQn              =   0,      /* User software interrupt */
    Supervisor_Software_IRQn        =   1,      /* Supervisor software interrupt */
    Machine_Software_IRQn           =   3,      /* Machine software interrupt */
    User_Timer_IRQn                 =   4,      /* User timer interrupt */
    Supervisor_Timer_IRQn           =   5,      /* Supervisor timer interrupt */
    CORET_IRQn                      =   7,      /* core Timer Interrupt */
    GPIO0_IRQn                      =   16,     /* uart Interrupt */
    TIM0_IRQn                       =   17,     /* timer0 Interrupt */
    TIM1_IRQn                       =   18,     /* timer1 Interrupt */
    TIM2_IRQn                       =   19,     /* timer2 Interrupt */
    TIM3_IRQn                       =   20,     /* timer3 Interrupt */
    TIM4_IRQn                       =   21,     /* timer4 Interrupt */
    TIM5_IRQn                       =   22,     /* timer5 Interrupt */
    TIM6_IRQn                       =   23,     /* timer6 Interrupt */
    TIM7_IRQn                       =   24,     /* timer7 Interrupt */
    PWM_IRQn                        =   25,     /* pwm Interrupt */
    RTC_IRQn                        =   26,     /* rtc Interrupt */
    WDT_IRQn                        =   27,     /* wdt Interrupt */
    USI0_IRQn                       =   28,     /* usi0 Interrupt */
    USI1_IRQn                       =   29,     /* usi1 Interrupt */
    USI2_IRQn                       =   30,     /* usi2 Interrupt */
    PMU_IRQn                        =   31,     /* pmu Interrupt */
    DMAC0_IRQn                      =   32,     /* dmac0 Interrupt */
    TIM8_IRQn                       =   33,     /* timer8 Interrupt */
    TIM9_IRQn                       =   34,     /* timer9 Interrupt */
    TIM10_IRQn                       =  35,     /* timer10 Interrupt */
    TIM11_IRQn                       =  36,     /* timer11 Interrupt */
    TIM12_IRQn                       =  37,     /* timer12 Interrupt */
    TIM13_IRQn                       =  38,     /* timer13 Interrupt */
    TIM14_IRQn                       =  39,     /* timer14 Interrupt */
    TIM15_IRQn                       =  40,     /* timer15 Interrupt */
    USI0_IRQn_myself                = 41 ,
}
IRQn_Type;

3)devices中,sg_usi_config,新增

const sg_usi_config[CONFIG_USI_NUM] = {
    {WJ_USI0_BASE, USI0_IRQn, USI0_IRQHandler},
    {WJ_USI1_BASE, USI1_IRQn, USI1_IRQHandler},
    {WJ_USI2_BASE, USI2_IRQn, USI2_IRQHandler},
    {WJ_USI0_BASE_myself, USI0_IRQn_myself, USI0_IRQHandler},
};

4)soc.h,更改CONFIG_USI_NUM

#define CONFIG_USI_NUM 4

5)devices.c中,新增

extern void USI0_IRQHandler(void);
extern void USI1_IRQHandler(void);
extern void USI2_IRQHandler(void);
extern void USI0_IRQHandler_myself(void);

6)isr.c中,新增

ATTRIBUTE_ISR void USI0_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(0);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI1_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(1);
    CSI_INTRPT_EXIT();
}

ATTRIBUTE_ISR void USI2_IRQHandler(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(2);
    CSI_INTRPT_EXIT();
}
ATTRIBUTE_ISR void USI0_IRQHandler_myself(void)
{
    CSI_INTRPT_ENTER();
    wj_usi_irqhandler(3);
    CSI_INTRPT_EXIT();
}

7)pin.h

#define CONSOLE_IDX     3

四、验证

终于SDK也修改完成,我们要验证一下是否正确了。流程和本系列的文章③一样,看一下打印出来的是否正确就好了。

欢迎留言讨论。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:43:47  更:2022-03-12 17:44:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:49:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码