博流bl616开发笔记

本文大体框架如图

目录

一、博流BL616、BL618基本框架、信息

BL618开发板可以进行BL618芯片的快速评估、开发。开发板集成了CK-Link调试器,无需额外的调试工具。BL616/BL618 是一款博流智能推出的基于RISC-V 架构 的32-bit CPU,适用于超低功耗 应用的 Wi-Fi 6+蓝牙5.x+Zigbee 组合芯片。支持 RISC-V 32/16 位混编指令集,包含64个外部中断源 ,有4个bits可以用于配置中断优先级。开发板主要包含:BL618核心电路、CK-Link调试器、TF卡座、Flash存储器、USB 2.0 HS OTG、IPEX天线座、IO引出、用户按键及LED。

二、博流烧录环境搭建

1. Windows环境

1.1 SDK

博流BL618 VMG0 SDK

1.2 编译工具链

步骤一

安装Gitee仓库
步骤二

Gitee的命令串口敲击

//下载编译工具链
git clone git@gitee.com:bouffalolab/toolchain_gcc_t-head_windows.git		

 //下载bl_mcu_sdk指令(如果在上一步中已经下载则不需要执行)
git clone https://github.com/bouffalolab/bl_mcu_sdk.git                    

步骤三

环境变量添加工具链路径、cmake路径、make路径

按下述图片步骤配置:

在新建中加入,上述的三个路径

步骤四

验证路径是否添加成功:cmd命令行窗口下输入下述命令查询

//查询交叉编译工具链的版本号
riscv64-unknown-elf-gcc --version

//查询cmake版本号
cmake --version

//查询make版本号
make --version

1.3 开发工具

工具一:eclipse

工具二:vscode

ecilpse使用调试功能需要下载调试软件:调试软件T-head

vscode安装时注意勾选:

则以后使用vscode时可以用右键点击文件夹选择vscode的打开方式。

1.4 程序编译下载

1.4.1 eclipse使用步骤

第一步

设置工作空间

第二步

打开工作工程文件夹(ps:与工作空间不能重合)


第三步

编译、下载

Build Target 修改要编译的对象

Build Command 修改硬件平台(如果使用的是bl616g0,在这里修改)

(修改硬件平台也可以在工程的Makefile中修改)

然后查找硬件连接的电脑COM口,加入到Build Command中

修改完后双击make开始编译
双击flash开始下载

第四步

调试

打开调试软件T-head后

点击eclipse菜单栏中的Debug >> Debug Config

在C/C++ application 中选择调试路径

双击Debug即可调试

1.4.2 vscode使用步骤

第一步

进入vscode打开工程文件后:打开终端

第二步

使用命令行指令进行操作

//进入文件夹
cd '文件夹路径'

//清除编译(工程文件目录下输入)
make clean

//编译(根据板子是bl616gk还是bl616g0现在板子型号)
make CHIP=bl616 BOARD=bl616g0

//下载(com14工具接口情况而变化,可能是其他com口)
make flash COMX=com14

板子型号可以在Makefile中修改

BL_SDK_BASE是工程文件的路径,需要我们每次新建工程文件时都要修改。

2. Linux环境

目前为笔者根据网上文章整合的材料,未经实验,谨慎参考

步骤一

Linux 下可直接使用命令行安装git

$ sudo apt-get update
$ sudo apt-get upgrade -y
$ sudo apt-get install -y git

步骤二

现在mdk文件夹

//下载编译工具链
git clone git@gitee.com:bouffalolab/toolchain_gcc_t-head_windows.git		

步骤三

确认依赖是否安装完全

make 、 cmake 、 Ninja

sudo apt-get install -y build-essential cmake ninja-build

安装、配置完成后,在命令行中测试是否安装成功

//查询Ninja版本号
Ninja --version

//查询cmake版本号
cmake --version

//查询make版本号
make --version

步骤四

下载交叉编译器
工具包链接

下载后解压至 /opt 目录下

sudo tar zxvf Xuantie-900-gcc-elf-newlib-x86_64-V2.6.1-20220906.tar.gz -C /opt

然后将交叉编译器路径添加至编译变量中,使用 zsh 的可以编辑 ~/.zshrc 文件;使用 bash 的可以编辑 ~/.bash 文件。

$ vim ~/.zshrc
# 在空白处加入一行
export PATH=$PATH:/opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.6.1/bin

保存后,使用 source 命令让配置生效。

source ~/.zshrc

查询是否配置成功

//查询交叉编译工具链的版本号
riscv64-unknown-elf-gcc --version

步骤五

编译、下载

使用的也是vscode,Linux版本下载步骤连接

三、基本外设使用

前言

在使用所有硬件前需要调用板子硬件的初始化函数:board_init();

需要调用头文件:#include "board.h"

1.GPIO

官方手册

1.1 硬件原理图

通过硬件原理图查找GPIO的引脚号:如图

从图片上我们需要找到可以用的GPIO端口(没有进行其他功能的应用的GPIO口)

可以放心使用的端口:GPIO 0 ~ 1 和GPIO 3 ~ 15以及GPIO18 ~ 34

1.2 API

GPIO控制的API:

实现需要调用头文件:

c 复制代码
#include "bflb_gpio.h"
1.2.1句柄

在讲GPIO的API应用前,我们需要了解bl616的硬件操作方式,通过句柄进行操作

博流的GPIO是通过句柄调用来实现控制的。

在头文件中厂家已经帮我们打包好了句柄结构体,可以通过我们需要调用的GPIO的name来调出我们需要使用的句柄。

获取句柄API:bflb_device_get_by_name

c 复制代码
//函数在  bflb_core.h  和  bflb_core.c  
//使用下述函数需调用:
//#include "bflb_core.h"

//句柄结构体
struct bflb_device_s {
    const char *name;
    uint32_t reg_base;
    uint8_t irq_num;
    uint8_t idx;
    uint8_t sub_idx;
    uint8_t dev_type;
    void *user_data;
};

//句柄获取函数,通过name来查找
/**
 * @brief Get device handle by name.
 *
 * @param [in] name device name
 * @return device handle
 */
struct bflb_device_s *bflb_device_get_by_name(const char *name);
1.2.2 GPIO的API

了解句柄后,我们有一下几个与句柄操作相关的API需要了解:

1.初始化函数:bflb_gpio_init

2.GPIO使能高电平:bflb_gpio_set

3.GPIO使能低电平:bflb_gpio_reset

4.GPIO读取电平函数:bflb_gpio_read

c 复制代码
//使用下述函数需调用:
//#include "bflb_gpio.h"

/**
 * @brief Initialize gpio pin.
 *
 * @param [in] dev device handle
 * @param [in] pin gpio pin, use @ref GPIO_PIN
 * @param [in] cfgset gpio config mask
 */
void bflb_gpio_init(struct bflb_device_s *dev, uint8_t pin, uint32_t cfgset);

/**
 * @brief Write gpio pin with high level.
 *
 * @param [in] dev device handle
 * @param [in] pin gpio pin, use @ref GPIO_PIN
 */
void bflb_gpio_set(struct bflb_device_s *dev, uint8_t pin);

/**
 * @brief Write gpio pin with low level.
 *
 * @param [in] dev device handle
 * @param [in] pin gpio pin, use @ref GPIO_PIN
 */
void bflb_gpio_reset(struct bflb_device_s *dev, uint8_t pin);

/**
 * @brief Read level from gpio pin.
 *
 * @param [in] dev device handle
 * @param [in] pin gpio pin, use @ref GPIO_PIN
 * @return true means high level, otherwise low level
 */
bool bflb_gpio_read(struct bflb_device_s *dev, uint8_t pin);

1.3 应用

以下是通过验证的实现代码

c 复制代码
    #include "bflb_gpio.h"

    struct bflb_device_s *gpio;

    extern void board_init(void);

    int main(void)
    {
        board_init();

        gpio = bflb_device_get_by_name("gpio");
        printf("gpio output\r\n");
        bflb_gpio_init(gpio, GPIO_PIN_32, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
        bflb_gpio_init(gpio, GPIO_PIN_24, GPIO_INPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

        while (1) {
            bflb_gpio_set(gpio, GPIO_PIN_32);
            printf("%x\r\n", bflb_gpio_read(gpio, GPIO_PIN_24));
            bflb_mtimer_delay_ms(2000);

            bflb_gpio_reset(gpio, GPIO_PIN_32);
            printf("%x\r\n", bflb_gpio_read(gpio, GPIO_PIN_24));
            bflb_mtimer_delay_ms(2000);
        }
    }

2.串口

官方手册

2.1 串口硬件

依照官方简介在bl_mcu_sdk -> bsp -> board -> bl616dk -> board.c中可以看到, 这块板子的UART_TX和UART_RX分别被分配给引脚23和24。

在博流系列芯片中,有个 UART SIG 的概念, 每个 SIG 对应到 UART 所有功能,功能类似于 8选1 选择器或者 12 选1 选择器。并且每个 SIG 都有一个默认的 UART 功能,如图所示:

2.2 串口调用API

首先需要定义头文件#include "bflb_uart.h"

1.串口初始化函数(即上述函数):board_uartx_gpio_init;

2.串口功能初始化函数:bflb_uart_init

3.串口接收一个字节函数:bflb_uart_getchar

4.串口发送一个字节函数:bflb_uart_putchar

对于获取句柄的函数的参数我们可以选择:

c 复制代码
//在hardware/uart_reg.h文件中可以找到句柄名的宏定义
struct bflb_device_s bl602_device_table[] = {
    { .name = "adc",
      .reg_base = AON_BASE,
      .irq_num = BL602_IRQ_GPADC_DMA,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_ADC,
      .user_data = NULL },
    { .name = "dac",
      .reg_base = GLB_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_DAC,
      .user_data = NULL },
    { .name = "ef_ctrl",
      .reg_base = EF_CTRL_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_EF_CTRL,
      .user_data = NULL },
    { .name = "gpio",
      .reg_base = GLB_BASE,
      .irq_num = BL602_IRQ_GPIO_INT0,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_GPIO,
      .user_data = NULL },
    { .name = "uart0",
      .reg_base = UART0_BASE,
      .irq_num = BL602_IRQ_UART0,
      .idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_UART,
      .user_data = NULL },
    { .name = "uart1",
      .reg_base = UART1_BASE,
      .irq_num = BL602_IRQ_UART1,
      .idx = 1,
      .dev_type = BFLB_DEVICE_TYPE_UART,
      .user_data = NULL },
    { .name = "spi0",
      .reg_base = SPI_BASE,
      .irq_num = BL602_IRQ_SPI0,
      .idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_SPI,
      .user_data = NULL },
    { .name = "pwm0",
      .reg_base = PWM_BASE,
      .irq_num = BL602_IRQ_PWM,
      .idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_PWM,
      .user_data = NULL },
    { .name = "dma0_ch0",
      .reg_base = DMA_BASE + 1 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch1",
      .reg_base = DMA_BASE + 2 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 1,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch2",
      .reg_base = DMA_BASE + 3 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 2,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch3",
      .reg_base = DMA_BASE + 4 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 3,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch4",
      .reg_base = DMA_BASE + 5 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 4,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch5",
      .reg_base = DMA_BASE + 6 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 5,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch6",
      .reg_base = DMA_BASE + 7 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 6,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "dma0_ch7",
      .reg_base = DMA_BASE + 8 * DMA_CHANNEL_OFFSET,
      .irq_num = BL602_IRQ_DMA0_ALL,
      .idx = 0,
      .sub_idx = 7,
      .dev_type = BFLB_DEVICE_TYPE_DMA,
      .user_data = NULL },
    { .name = "i2c0",
      .reg_base = I2C_BASE,
      .irq_num = BL602_IRQ_I2C0,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_I2C,
      .user_data = NULL },
    { .name = "timer0",
      .reg_base = TIMER_BASE,
      .irq_num = BL602_IRQ_TIMER0,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_TIMER,
      .user_data = NULL },
    { .name = "timer1",
      .reg_base = TIMER_BASE,
      .irq_num = BL602_IRQ_TIMER1,
      .idx = 1,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_TIMER,
      .user_data = NULL },
    { .name = "rtc",
      .reg_base = HBN_BASE,
      .irq_num = BL602_IRQ_HBN_OUT0,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_RTC,
      .user_data = NULL },
    { .name = "aes",
      .reg_base = SEC_ENG_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_AES,
      .user_data = NULL },
    { .name = "sha",
      .reg_base = SEC_ENG_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_SHA,
      .user_data = NULL },
    { .name = "trng",
      .reg_base = SEC_ENG_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_TRNG,
      .user_data = NULL },
    { .name = "pka",
      .reg_base = SEC_ENG_BASE,
      .irq_num = 0xff,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_PKA,
      .user_data = NULL },
    { .name = "watchdog",
      .reg_base = TIMER_BASE,
      .irq_num = BL602_IRQ_WDT,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_TIMER,
      .user_data = NULL },
    { .name = "irtx",
      .reg_base = IR_BASE,
      .irq_num = BL602_IRQ_IRTX,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_IR,
      .user_data = NULL },
    { .name = "irrx",
      .reg_base = IR_BASE,
      .irq_num = BL602_IRQ_IRRX,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_IR,
      .user_data = NULL },
    { .name = "sdio2",
      .reg_base = SDU_BASE,
      .irq_num = BL602_IRQ_SDIO,
      .idx = 0,
      .sub_idx = 0,
      .dev_type = BFLB_DEVICE_TYPE_SDIO2,
      .user_data = NULL },
};
c 复制代码
//从初始化功能函数开始:
//在hardware/uart_reg.h文件中可以找到相关的宏定义参数
//参数中用到的结构体类型:
/**
 * @brief UART configuration structure
 *
 * @param baudrate          UART波特率设置,单位为bps,应小于uart_clk/2
 * @param direction         UART方向,使用@ref UART_DIRECTION
 * @param data_bits         UART数据位,使用@ref UART_DATABITS
 * @param stop_bits         UART停止位,使用@ref UART_STOPBITS
 * @param parity            UART奇偶位,使用@ref UART_PARITY
 * @param bit_order         UART位先用@ref UART_BITORDER
 * @param flow_ctrl         UART流量控制设置,使用@ref UART_FLOWCTRL
 * @param tx_fifo_threshold UART tx fifo 阈值,应该小于32。.
 * @param rx_fifo_threshold UART rx fifo 阈值,应该小于32。
 */
struct bflb_uart_config_s {
    uint32_t baudrate;
    uint8_t direction;
    uint8_t data_bits;
    uint8_t stop_bits;
    uint8_t parity;
    uint8_t bit_order;
    uint8_t flow_ctrl;
    uint8_t tx_fifo_threshold;
    uint8_t rx_fifo_threshold;
};

/**
 * @brief Put one char on uart.
 *
 * @param [in] dev device handle
 * @param [in] ch char
 * @return A negated errno value on failure.
 */
int bflb_uart_putchar(struct bflb_device_s *dev, int ch);

/**
 * @brief Get char from uart.
 *
 * @param [in] dev device handle
 * @return A negated errno value on failure, otherwise received char.
 */
int bflb_uart_getchar(struct bflb_device_s *dev);

2.3 应用

c 复制代码
#include "bflb_mtimer.h"
#include "bflb_uart.h"
#include "board.h"

struct bflb_device_s *uartx;

int main(void)
{
    board_init();
    board_uartx_gpio_init();

    uartx = bflb_device_get_by_name(DEFAULT_TEST_UART);

    struct bflb_uart_config_s cfg;

    cfg.baudrate = 2000000;
    cfg.data_bits = UART_DATA_BITS_8;
    cfg.stop_bits = UART_STOP_BITS_1;
    cfg.parity = UART_PARITY_NONE;
    cfg.flow_ctrl = 0;
    cfg.tx_fifo_threshold = 7;
    cfg.rx_fifo_threshold = 7;
    bflb_uart_init(uartx, &cfg);

    int ch;
    while (1) {
        ch = bflb_uart_getchar(uartx);
        if (ch != -1) {
            bflb_uart_putchar(uartx, ch);
        }
    }
}

3.定时器

官方手册(没找到中文版,只有英文版)

3.1硬件资源

博流618内部有两个定时器,分别为定时器1和定时器0,每个定时器里会有三个比较器,每个比较器的值到达的时候就会产生硬件中断。

bl616时钟框架图:

3.2API使用

在使用API前我们需要先认识一个定时器功能初始化的结构体

c 复制代码
//该结构体存在于bflb_timer.h中

/**
 * @brief TIMER configuration structure
 *
 * @param counter_mode      Timer counter mode, use @ref TIMER_COUNTER_MODE
 * @param clock_source      Timer clock source, use @ref TIMER_CLK_SOURCE
 * @param clock_div         Timer clock divison value, from 0 to 255
 * @param trigger_comp_id   Timer count register preload trigger source slelect, use @ref TIMER_COMP_ID
 * @param comp0_val         Timer compare 0 value
 * @param comp1_val         Timer compare 1 value
 * @param comp2_val         Timer compare 2 value
 * @param preload_val       Timer preload value
 */
struct bflb_timer_config_s {
    uint8_t counter_mode;
    uint8_t clock_source;
    uint8_t clock_div;
    uint8_t trigger_comp_id;
    uint32_t comp0_val;
    uint32_t comp1_val;
    uint32_t comp2_val;
    uint32_t preload_val;
};

接下来是timer的API函数认识

1.获取相应比较id的定时器中断状态函数:bflb_timer_get_compint_status

2.清除对应比较id的定时器中断状态函数:bflb_timer_compint_clear

3.初始化定时器函数:bflb_timer_init

4.使能定时器函数:bflb_timer_start

c 复制代码
/**
 * @brief Get timer interrupt status of corresponding compare id.
 *
 * @param [in] dev device handle
 * @param [in] cmp_no compare id, use @ref TIMER_COMP_ID
 * @return true mean yes, otherwise no.
 */
bool bflb_timer_get_compint_status(struct bflb_device_s *dev, uint8_t cmp_no);

/**
 * @brief Clear timer interrupt status of corresponding compare id.
 *
 * @param [in] dev device handle
 * @param [in] cmp_no compare id, use @ref TIMER_COMP_ID
 */
void bflb_timer_compint_clear(struct bflb_device_s *dev, uint8_t cmp_no);

/**
 * @brief Initialize timer.
 *
 * @param [in] dev device handle
 * @param [in] config pointer to save timer config
 */
void bflb_timer_init(struct bflb_device_s *dev, const struct bflb_timer_config_s *config);

/**
 * @brief Start timer.
 *
 * @param [in] dev device handle
 */
void bflb_timer_start(struct bflb_device_s *dev);

因为定时器还要使用中断配合

1.中断连接函数:bflb_irq_attach

2.中断使能函数:bflb_irq_enable

c 复制代码
//在bflb_irq.h中定义
/**
 * @brief Attach interrupt with callback.
 *
 * @param [in] irq irq number
 * @param [in] isr interrupt callback
 * @param [in] arg user data
 * @return A negated errno value on failure.
 */
 //typedef void (*irq_callback)(int irq, void *arg);
 //irq_callback是一个函数指针,指向一个自定义的同类型函数地址
int bflb_irq_attach(int irq, irq_callback isr, void *arg);

/**
 * @brief Enable interrupt.
 *
 * @param [in] irq irq number
 */
void bflb_irq_enable(int irq);

3.3 应用

c 复制代码
#include "bflb_mtimer.h"
#include "bflb_timer.h"
#include "board.h"

#define TEST_TIMER_COMP_ID TIMER_COMP_ID_2

struct bflb_device_s *timer0;
struct bflb_device_s *timer1;

void timer0_isr(int irq, void *arg)
{
    bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
        printf("timer0 comp0 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_1);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_1);
        printf("timer0 comp1 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_2);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_2);
        printf("timer0 comp2 trigger\r\n");
    }
}

void timer1_isr(int irq, void *arg)
{
    bool status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_0);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_0);
        printf("timer1 comp0 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_1);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_1);
        printf("timer1 comp1 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_2);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_2);
        printf("timer1 comp2 trigger\r\n");
    }
}

int main(void)
{
    board_init();
    printf("Timer basic test\n");

    /* timer clk = XCLK/(div + 1 )*/
    struct bflb_timer_config_s cfg0;
    cfg0.counter_mode = TIMER_COUNTER_MODE_PROLOAD; /* preload when match occur */
    cfg0.clock_source = TIMER_CLKSRC_XTAL;
    cfg0.clock_div = 39; /* for bl616/bl808/bl606p is 39, for bl702 is 31 */
    cfg0.trigger_comp_id = TEST_TIMER_COMP_ID;
    cfg0.comp0_val = 1000000; /* match value 0  */
    cfg0.comp1_val = 1500000; /* match value 1 */
    cfg0.comp2_val = 2500000; /* match value 2 */
    cfg0.preload_val = 0;    /* preload value */

    struct bflb_timer_config_s cfg1;
    cfg1.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
    cfg1.clock_source = TIMER_CLKSRC_XTAL;
    cfg1.clock_div = 39; /* for bl616/bl808/bl606p is 39, for bl702 is 31 */
    cfg1.trigger_comp_id = TEST_TIMER_COMP_ID;
    cfg1.comp0_val = 1000000; /* match value 0  */
    cfg1.comp1_val = 1500000; /* match value 1 */
    cfg1.comp2_val = 2500000; /* match value 2 */
    cfg1.preload_val = 0;    /* preload value */

    timer0 = bflb_device_get_by_name("timer0");
    timer1 = bflb_device_get_by_name("timer1");

    /* Timer init with default configuration */
    bflb_timer_init(timer0, &cfg0);
    bflb_timer_init(timer1, &cfg1);

    bflb_irq_attach(timer0->irq_num, timer0_isr, NULL);
    bflb_irq_attach(timer1->irq_num, timer1_isr, NULL);
    bflb_irq_enable(timer0->irq_num);
    bflb_irq_enable(timer1->irq_num);

    /* Enable timer */
    bflb_timer_start(timer0);
    bflb_timer_start(timer1);

    printf("case success.\r\n");
    while (1) {
        bflb_mtimer_delay_ms(1500);
    }
}

4.Wi-Fi

官方手册

4.1硬件资源

WiFi时钟源:

4.2应用

4.2.1 WIFI应用

通过查找以WiFi命名的文件,可以看到该SDK将WiFi的初始化函数封装成了.a的链接文件,所以我只对他的函数使用进行了学习,并为深究其代码实现。

连接WiFi的指令

//运行WiFi代码后:
wifi_sta_connect "wifi name" "密码"

连接WiFi时的代码:

c 复制代码
    switch (code) {
        case CODE_WIFI_ON_INIT_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_INIT_DONE\r\n", __func__);
            wifi_mgmr_init(&conf);
        } break;
        case CODE_WIFI_ON_MGMR_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_MGMR_DONE\r\n", __func__);
        } break;
        case CODE_WIFI_ON_SCAN_DONE: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_SCAN_DONE\r\n", __func__);
            wifi_mgmr_sta_scanlist();
        } break;
        case CODE_WIFI_ON_CONNECTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_CONNECTED\r\n", __func__);
            void mm_sec_keydump();
            mm_sec_keydump();
        } break;
        case CODE_WIFI_ON_GOT_IP: {
            wifi_state = 1;
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_GOT_IP\r\n", __func__);
            LOG_I("[SYS] Memory left is %d Bytes\r\n", kfree_size());
        } break;
        case CODE_WIFI_ON_DISCONNECT: {
            wifi_state = 0;
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_DISCONNECT\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STARTED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STARTED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STOPPED: {
            LOG_I("[APP] [EVT] %s, CODE_WIFI_ON_AP_STOPPED\r\n", __func__);
        } break;
        case CODE_WIFI_ON_AP_STA_ADD: {
            LOG_I("[APP] [EVT] [AP] [ADD] %lld\r\n", xTaskGetTickCount());
        } break;
        case CODE_WIFI_ON_AP_STA_DEL: {
            LOG_I("[APP] [EVT] [AP] [DEL] %lld\r\n", xTaskGetTickCount());
        } break;
        default: {
            LOG_I("[APP] [EVT] Unknown code %u \r\n", code);
        }
    }
4.2.2 Onenet MQTT为例

因为MQTT协议下的互联网平台接入可以适配不同的基于不同物联网的设备开发,所以MQTT接入Onenet的应用为例,学习博流芯片wifi接入功能。
视频教程
Onenet mqtt产品创建
Onenet产品开发网站

点击产品开发,根据需求选择产品品类,并选择设备接入

选择MQTT协议和OneJson

然后加入设备,以及如果是使用自定义方案得自己在产品设置中加入自定义物理模,没用过云平台开发的建议先将标准方案跑通。

这些比较基础就不一一体现了。

MQTT接入方式
官方指导文档:文档中心=》产品开发=》设备接入=》设备开发=》MQTT协议接入=》MQTT设备连接

MQTTX

创建完后,我们需要先下载一个软件------MQTTX,是用来模拟MQTT协议接入云平台过程的软件,可以帮助我们连接MQTT协议接入平台的流程
下载网站

name中填写产品ID,Client ID是设备名

Host与Port分别是我们需要连接的服务器地址和服务器端口:

Onene为例在官方文档中找到:

UserName是产品ID

Password密码需要使用token计算工具计算:

res中products跟随着的是我们的产品ID,所以需要我们修改。

et是生成的秘钥有效时间戳。

key是产品秘钥。

订阅、发布

连接上服务器后,需要我们先进行订阅产品的Topic,然后就可以通过改软件进行Josn格式的信息互传了

MQTTX订阅方式:在NewSnbscription中订阅我们需要的功能的Topic

在这里查找我们要用到的topic,但要记得修改产品ID

需要修改产品名、产品ID、功能标识符和数据。

bl616的实现

使用MDK包中的例程:onenet_mqtt_sub文件夹中:

修改onenet_mqtt_sub.c文件中的

#define ADDRESS     "183.230.40.96"		//可以通过域名ping出来
#define PORT        "1883"
#define CLIENTID    "XXXXXXX"  //设备的名称
#define USERNAME    "XXXXXXX"  //产品ID
#define PASSWORD    "XXXXXXXX"//使用onennet的token工具生成密码 工具地址 https://open.iot.10086.cn/doc/v5/fuse/detail/242
#define TOPIC       "$sys/XXXXXX/???????/thing/property/set"  //订阅topic  XXXXXX是产品ID   ??????是设备名称

5.AUDAC

5.1 硬件资源


基本框图

FIFO格式

有四种模式,分别决定有效数据的最高位在哪一位。

当待播放的音频文件是 16bit 的宽度时,选择 Mode3 即可。因为我们 DAC 的最大分辨率就是16bits。其他模式存在的意义在于,如果待播放的音频文件宽度为 32/24/20 Bits 时,用户需要做出取舍,将低位的一些信息裁剪掉,以保证后级电路获得的是 16bits 宽度的数据,这里默认是将低位舍弃。

DMA搬运

AWPWM 的 TX FIFO 数据可以通过 DMA 进行搬运。

用户可以通过 PDM_TX_FIFO_STATUS 寄存器实时获得目前 FIFO 有效数据的数量。

通过配置 FIFO_CTRL[15:14] 来选择发起 DMA request 的 FIFO count 阈值,是 8/16/32,或者是由 FIFO_CTRL[22:16]配置来决定。

当 count 的值大于设定阈值,并且 PDM_TX_FIFO_CTRL[12:8] 对应通路的 FIFO 被使能,则会发起一次 DMA 搬运。

注意,启动 TX FIFO 时,如果 TX FIFO 里面并没有有效的数据,则会触发 tx underrun 错误。因此要注意软件配置顺序。

5.2 应用

初始化DMA搬运:

c 复制代码
    struct bflb_dma_channel_config_s audac_dma_cfg;				

    audac_dma_cfg.direction = DMA_MEMORY_TO_PERIPH;				//内存到外设
    audac_dma_cfg.src_req = DMA_REQUEST_NONE;					
    audac_dma_cfg.dst_req = DMA_REQUEST_AUDAC_TX;				//AUDAC
    audac_dma_cfg.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
    audac_dma_cfg.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
    audac_dma_cfg.src_burst_count = DMA_BURST_INCR8;
    audac_dma_cfg.dst_burst_count = DMA_BURST_INCR8;
    audac_dma_cfg.src_width = DMA_DATA_WIDTH_16BIT;
    audac_dma_cfg.dst_width = DMA_DATA_WIDTH_16BIT;
    
    audac_dma_hd = bflb_device_get_by_name("dma0_ch0");
    bflb_dma_channel_init(audac_dma_hd, &audac_dma_cfg);
    bflb_dma_channel_irq_attach(audac_dma_hd, audio_dma_callback, NULL);

初始化DMA搬运链表

    uint32_t dma_lli_cnt;
    static struct bflb_dma_channel_lli_pool_s lli_pool[10];
    struct bflb_dma_channel_lli_transfer_s transfers[1];

    transfers[0].src_addr = (uint32_t)sin_0db_l32_r16_2ch;
    transfers[0].dst_addr = (uint32_t)DMA_ADDR_AUDAC_TDR;
    transfers[0].nbytes = sizeof(sin_0db_l32_r16_2ch);

    bflb_l1c_dcache_clean_range(sin_0db_l32_r16_2ch, sizeof(sin_0db_l32_r16_2ch));
    dma_lli_cnt = bflb_dma_channel_lli_reload(audac_dma_hd, lli_pool, 10, transfers, 1);
    bflb_dma_channel_lli_link_head(audac_dma_hd, lli_pool, dma_lli_cnt);							//链表循环模式

初始化AUDAC

	//初始化AUDAC接触功能
    struct bflb_audac_init_config_s audac_init_cfg = {
        .sampling_rate = AUDAC_SAMPLING_RATE_32K,
        .output_mode = AUDAC_OUTPUT_MODE_PWM,
        .source_channels_num = AUDAC_SOURCE_CHANNEL_DUAL,
        .mixer_mode = AUDAC_MIXER_MODE_ONLY_L,
        .data_format = AUDAC_DATA_FORMAT_16BIT,
        .fifo_threshold = 1,
    };

	//初始化AUDAC音量控制
    struct bflb_audac_volume_config_s audac_volume_cfg = {
        .mute_ramp_en = true,
        .mute_up_ramp_rate = AUDAC_RAMP_RATE_FS_32,
        .mute_down_ramp_rate = AUDAC_RAMP_RATE_FS_8,
        .volume_update_mode = AUDAC_VOLUME_UPDATE_MODE_RAMP,
        .volume_ramp_rate = AUDAC_RAMP_RATE_FS_128,
        .volume_zero_cross_timeout = AUDAC_RAMP_RATE_FS_128,
    };

    /* clock cfg */
    GLB_Config_AUDIO_PLL_To_491P52M();
    GLB_PER_Clock_UnGate(GLB_AHB_CLOCK_AUDIO);

    /* audac init */
    audac_hd = bflb_device_get_by_name("audac");
    bflb_audac_init(audac_hd, &audac_init_cfg);
    bflb_audac_feature_control(audac_hd, AUDAC_CMD_SET_VOLUME_VAL, (size_t)(-15 * 2));
    bflb_audac_volume_init(audac_hd, &audac_volume_cfg);
    
    /* audac enable dma */
    bflb_audac_link_rxdma(audac_hd, true);
相关推荐
呵呵哒( ̄▽ ̄)"1 小时前
尚硅谷-react教程-求和案例-优化3-整合UI组件和容器组件-总结优化-笔记
前端·笔记·react.js
l1x1n02 小时前
【IT基础中英笔记】符号系统与数据类型 | CompTIA ITF+
笔记·学习
kfepiza2 小时前
Win11GBK, idea2024.2.4, 使用Gradle8.8本地安装构建,不使用包装器, 解决utf-8乱码问题, 笔记241028
笔记·gradle·intellij-idea·idea·intellij idea
黄交大彭于晏3 小时前
第五天学习总结:C语言学习笔记 - 数组篇
c语言·笔记·学习
Pandaconda3 小时前
【计算机网络 - 基础问题】每日 3 题(五十九)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展
爱编程的古惑仔3 小时前
leetcode刷题笔记——15.三数之和
笔记·算法·leetcode
zhentiya4 小时前
哈工大《理论力学》第九版课后答案解析及笔记PDF
笔记·pdf
一心赚狗粮的宇叔4 小时前
java web调试时清理当前网址的缓存
java·前端·笔记·后端·缓存
呵呵哒( ̄▽ ̄)"4 小时前
尚硅谷-react教程-求和案例-最终版-笔记
笔记