一、流程

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_PANGUwj_usi_usart_irqhandler(idx);
#endifbreak;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_INIT120:     jal     board_init121  #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_SECUREcsi_vic_enable_sirq(irq_num);
#elsecsi_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也修改完成,我们要验证一下是否正确了。流程和本系列的文章③一样,看一下打印出来的是否正确就好了。

欢迎留言讨论。

无剑100SOC(wujian100)挂UART外设之④修改SDK相关推荐

  1. 无剑100SOCwujian100挂UART外设之③硬件挂UART

    终于来到了挂UART外设.首先是硬件层面挂上. 一.在哪 首先看无剑100SOC的代码,找到UART位于哪个位置.如下图,为无剑100的总线结构.AHB总线,有7个主机和12个从机.主机0.1.2为来 ...

  2. 阿里平头哥无剑100SOCwujian100挂UART外设之①将无剑100下载到gensys开发板

    一.前言 组里的布置的任务,给大项目的推进打个小小的基础.经过上学期一个月和这学期开学几周,终于解决.其实真正用在挂UART的时间不多,大部分时间都用在如何把无剑100SOC下载到gensys开发板和 ...

  3. 无剑100SOCwujian100挂UART外设之②跑通自带UART

    一.为什么 首先跑通自带的UART,有助于我们熟悉UART的使用. 二.是什么 下载官方的SDK示例,或者下载慕课的示例也可以.如下图所示,在while循环中,添加了printf("test ...

  4. 百行代码解读阿里 AloT 芯片平台无剑 100!

    作者 | 马超 责编 | 胡巍巍 出品 | CSDN(ID:CSDNnews)  今年以来我国IT厂商都在AIot的底层平台建设方面可谓是捷报频传,在操作系统方面有如像腾讯的Tiny OS.阿里的Al ...

  5. matlab如何花间,酌酒花间,磨针石上;倚剑天下,挂弓扶桑。赞颂的是谁

    韩琴1009的回答: 赞颂的是唐代大诗人李白.这句话出自郭沫若先生题江油李白纪念馆的楹联.这幅楹联每四个字都对应了李白的一首诗中的句子: 酌酒花间:出自唐代李白所作的<月下独酌四首·其一> ...

  6. 平头哥发布一站式芯片设计平台“无剑”,芯片设计成本降低50%

    导读:8 月 29 日,在上海举行的世界人工智能大会上,阿里巴巴旗下半导体公司平头哥发布 SoC 芯片平台"无剑".无剑是面向 AIoT 时代的一站式芯片设计平台,提供集芯片架构. ...

  7. 阿里平头哥发布AIoT芯片平台“无剑”,可将芯片设计成本降低50%

    允中 发自 凹非寺  量子位 报道 | 公众号 QbitAI 阿里芯片,又有大动作.这次不是AI芯片,而是芯片平台. 世界AI大会期间,平头哥发布AIoT芯片平台,命名"无剑". ...

  8. (61)UART外设驱动接收驱动(六)(第13天)

    (61)UART外设驱动接收驱动(六)(第13天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)UART外设驱动接收驱动(六)(第13天) 5)技术交流 6)参 ...

  9. (60)UART外设驱动发送驱动(五)(第12天)

    (60)UART外设驱动发送驱动(五)(第12天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)UART外设驱动发送驱动(五)(第12天) 5)技术交流 6)参 ...

最新文章

  1. linux新用户登陆密码,如何强制Linux用户在第一次登录时更改初始密码?
  2. 001_Redis介绍
  3. css中超级链接样式的设置顺序
  4. 使用Dockerfile构建SpringBoot应用镜像
  5. 如何免费试用SAP的Fiori应用
  6. 如何优雅的绘制一棵省市区三级可选择的树?
  7. Ios 12 linux,苹果发布iOS 12.4.1,以修补越狱漏洞
  8. VS Code 下载
  9. 太原市中考计算机考试系统,太原中考报名系统
  10. 等宽分箱_数据分析师-数据挖掘如何分箱以及对箱子中的数据进行平滑处理
  11. 【BZOJ2659】算不出的算式,打表找规律
  12. 【报告分享】2020快手短视频直播电商营销增长宝典.pdf(附下载链接)
  13. 记一次Mysql查询字段为空串在java中使用equals不相等的问题
  14. access 套用表格_将ACCESS数据库导入到EXCEL表格
  15. 深鸿会深大小组学习笔记:第二周,从零开发鸿蒙小游戏2048app(下)
  16. 3DMAX哪个版本最稳定?3DMAX哪个版本最好用?
  17. 基于ISO27001的数据中心信息安全管理体系
  18. 哄女朋友玩的c语言编程,哄女朋友开心的小套路 逗女朋友开心的话套路
  19. 自学Java!三面蚂蚁核心金融部,Java岗
  20. 高端数据中心交换机散热系统大比拼

热门文章

  1. c语言任伟,任 伟
  2. 51单片机两只老虎 c语言,基于51单片机做音乐盒(两只老虎)
  3. 常用数据结构和算法总结
  4. 关于Python爬虫之获取海量表情包+存入数据库+搭建网站通过关键字查询表情包
  5. 数据库基础考点笔记-3
  6. java 类一定要声明成public_关于使用public class 和 class声明类的区别
  7. T100 genero report (GR) 凭证报表开发流程
  8. Steering Behaviors
  9. 企业级应用Service Management Automation X(SMAX)的微服务之路
  10. Java GUI气泡诗词02