版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_64069585/article/details/130900203
————————————————
版权声明:本文为CSDN博主「maeve1228」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_64069585/article/details/130900203
嵌入式开发(BL618芯片)定时器中断建构
概要
选择的开发环境为:vscode
开发板为:BL618
串口调试助手
整体架构流程
vscode程序开发编译步骤:
使用make CHIP=bl616
使用make flash COMX=comX(X是你开发板链接的端口,打开设备管理器查看即可)
使用riscv64-unknown-elf-gdb进行命令行下的调试,进入gdb进行详细配置
技术细节
链接好开发板之后进入vscode,编写一个c程序,例如
#include "bflb_mtimer.h"
#include "board.h"
#define DBG_TAG "MAIN"
#include "log.h"
int main(void)
{
board_init();
while (1) {
LOG_F("hello world fatal\r\n");
LOG_E("hello world error\r\n");
LOG_W("hello world warning\r\n");
LOG_I("hello world information\r\n");
LOG_D("hello world debug\r\n");
LOG_T("hello world trace\r\n");
LOG_RF("hello world fatal raw\r\n");
LOG_RE("hello world error raw\r\n");
LOG_RW("hello world warning raw\r\n");
LOG_RI("hello world information raw\r\n");
LOG_RD("hello world debug raw\r\n");
LOG_RT("hello world trace raw\r\n");
bflb_mtimer_delay_ms(1000);
}
}
然后将文件目录定位到当前目录,在vscode里,可以直接用powershell或者可以打开终端来进行以下操作:
make CHIP=bl616
使用make flash COMX=com5(我的端口号为5)
接下来打开串口调试助手,确认好端口等,这里有一点很重要的是:波特率的设定,必须设置为2000000(否则会出现乱码,别问我怎么知道的呜呜呜)
然后确定好之后点击开发板上的RST复位键,就输出内容了
在powershell里面进行命令编写的时候,我们也可以换个写法:
- make CHIP=bl618 BOARD=bl618g0
- make flash COMX=com5
同样的,打开串口调试器,你依旧可以得到以上一样的结果
开发板设备代码详细信息
#include "bflb_core.h"
#include "bl616_memorymap.h"
#include "bl616_irq.h"
#define DMA_CHANNEL_OFFSET 0x100
struct bflb_device_s bl616_device_table[] = {
{ .name = "adc",
.reg_base = AON_BASE,
.irq_num = BL616_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 = BL616_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 = BL616_IRQ_UART0,
.idx = 0,
.dev_type = BFLB_DEVICE_TYPE_UART,
.user_data = NULL },
{ .name = "uart1",
.reg_base = UART1_BASE,
.irq_num = BL616_IRQ_UART1,
.idx = 1,
.dev_type = BFLB_DEVICE_TYPE_UART,
.user_data = NULL },
{ .name = "spi0",
.reg_base = SPI_BASE,
.irq_num = BL616_IRQ_SPI0,
.idx = 0,
.dev_type = BFLB_DEVICE_TYPE_SPI,
.user_data = NULL },
{ .name = "pwm_v2_0",
.reg_base = PWM_BASE,
.irq_num = BL616_IRQ_PWM,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PWM,
.user_data = NULL },
{ .name = "dma0_ch0",
.reg_base = DMA_BASE + 1 * DMA_CHANNEL_OFFSET,
.irq_num = BL616_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 = BL616_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 = BL616_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 = BL616_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 = BL616_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 = BL616_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 = BL616_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 = BL616_IRQ_DMA0_ALL,
.idx = 0,
.sub_idx = 7,
.dev_type = BFLB_DEVICE_TYPE_DMA,
.user_data = NULL },
{ .name = "i2c0",
.reg_base = I2C0_BASE,
.irq_num = BL616_IRQ_I2C0,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_I2C,
.user_data = NULL },
{ .name = "i2c1",
.reg_base = I2C1_BASE,
.irq_num = BL616_IRQ_I2C1,
.idx = 1,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_I2C,
.user_data = NULL },
{ .name = "i2s0",
.reg_base = I2S_BASE,
.irq_num = BL616_IRQ_I2S,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_I2S,
.user_data = NULL },
{ .name = "timer0",
.reg_base = TIMER_BASE,
.irq_num = BL616_IRQ_TIMER0,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_TIMER,
.user_data = NULL },
{ .name = "timer1",
.reg_base = TIMER_BASE,
.irq_num = BL616_IRQ_TIMER1,
.idx = 1,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_TIMER,
.user_data = NULL },
{ .name = "rtc",
.reg_base = HBN_BASE,
.irq_num = BL616_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 = "emac0",
.reg_base = EMAC_BASE,
.irq_num = BL616_IRQ_EMAC,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_ETH,
.user_data = NULL },
{ .name = "watchdog",
.reg_base = TIMER_BASE,
.irq_num = BL616_IRQ_WDG,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_TIMER,
.user_data = NULL },
{ .name = "cks",
.reg_base = CKS_BASE,
.irq_num = 0,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_CKS,
.user_data = NULL },
{ .name = "mjpeg",
.reg_base = MJPEG_BASE,
.irq_num = BL616_IRQ_MJPEG,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_MJPEG,
.user_data = NULL },
{ .name = "irrx",
.reg_base = IR_BASE,
.irq_num = BL616_IRQ_IRRX,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_IR,
.user_data = NULL },
{ .name = "cam0",
.reg_base = DVP2AXI0_BASE,
.irq_num = BL616_IRQ_DVP2BUS_INT0,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_CAMERA,
.user_data = NULL },
{ .name = "cam1",
.reg_base = DVP2AXI1_BASE,
.irq_num = BL616_IRQ_DVP2BUS_INT1,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_CAMERA,
.user_data = NULL },
{ .name = "auadc",
.reg_base = AUADC_BASE,
.irq_num = BL616_IRQ_AUADC,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_AUDIOADC,
.user_data = NULL },
{ .name = "audac",
.reg_base = AUDAC_BASE,
.irq_num = BL616_IRQ_AUDAC,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_AUDIODAC,
.user_data = NULL },
{ .name = "sdio2",
.reg_base = SDU_BASE,
.irq_num = BL616_IRQ_SDIO,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_SDIO2,
.user_data = NULL },
{ .name = "dbi",
.reg_base = DBI_BASE,
.irq_num = BL616_IRQ_DBI,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_DBI,
.user_data = NULL },
{ .name = "plfm_dma_ch0",
.reg_base = PLFM_DMA_BASE,
.irq_num = 0,
.idx = 0,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PLFMDMA,
.user_data = NULL },
{ .name = "plfm_dma_ch1",
.reg_base = PLFM_DMA_BASE,
.irq_num = 0,
.idx = 1,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PLFMDMA,
.user_data = NULL },
{ .name = "plfm_dma_ch2",
.reg_base = PLFM_DMA_BASE,
.irq_num = 0,
.idx = 2,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PLFMDMA,
.user_data = NULL },
{ .name = "plfm_dma_ch3",
.reg_base = PLFM_DMA_BASE,
.irq_num = 0,
.idx = 3,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PLFMDMA,
.user_data = NULL },
{ .name = "plfm_dma_ch4",
.reg_base = PLFM_DMA_BASE,
.irq_num = 0,
.idx = 4,
.sub_idx = 0,
.dev_type = BFLB_DEVICE_TYPE_PLFMDMA,
.user_data = NULL },
};
struct bflb_device_s *bflb_device_get_by_name(const char *name)
{
for (uint8_t i = 0; i < sizeof(bl616_device_table) / sizeof(bl616_device_table[0]); i++) {
if (strcmp(bl616_device_table[i].name, name) == 0) {
return &bl616_device_table[i];
}
}
return NULL;
}
struct bflb_device_s *bflb_device_get_by_id(uint8_t type, uint8_t idx)
{
for (uint8_t i = 0; i < sizeof(bl616_device_table) / sizeof(bl616_device_table[0]); i++) {
if ((bl616_device_table[i].dev_type == type) && (bl616_device_table[i].idx = idx)) {
return &bl616_device_table[i];
}
}
return NULL;
}
void bflb_device_set_userdata(struct bflb_device_s *device, void *user_data)
{
device->user_data = user_data;
}
其中,我们可以根据每一块的基本数据内容来对设备进行访问,例如:
博流计时器
博流618内部有两个定时器,分别为定时器1和定时器0,每个定时器里会有三个比较器,每个比较器的值到达的时候就会产生硬件中断,那么我们把响应的中断来修改相应的GPIO的输出,由此可将两个样例结合。
首先完成gpio的配置:
计时器和比较器对gpio的代码:
#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);
}
}
经过flash编译执行之后,打开串口调试器,配置完成后点击开发板的复位键,进行输出
对上面四张图数据进行总结:
根据端口数据测量计算:
timer0的comp0和comp1 相差结果为:30.081-29.581=500微秒
观察代码cfg0.comp1-val-cfg0.comp0-val=500微秒
结果一致
通过观察其他数据的计算也可以得到与代码结果相吻合的效果,误差值很小。
(在定时器中断里去控制gpio,通过gpio的输出{波形图}去判断定时器的延时,目前我的逻辑分析仪还没有到手,到手之后数据测量完成再进行详细说明,目前就是我的代码测试数据)
#include "bflb_mtimer.h"
#include "bflb_timer.h"
#include "board.h"
#include"bflb_gpio.h"
#define TEST_TIMER_COMP_ID TIMER_COMP_ID_2
struct bflb_device_s *timer0;
// struct bflb_device_s *timer1;
struct bflb_device_s *gpio;
bool gpio_30=false;
bool gpio_31=false;
bool gpio_32=false;//来控制灯
void timer0_isr(int irq, void *arg)//timer0中端服务程序
{
bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);//0来打印这个
if (status) {
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
// printf("timer0 comp0 trigger\r\n");
gpio_30=!gpio_30;
}
status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_1);
if (status) {//1来打印这个
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_1);
// printf("timer0 comp1 trigger\r\n");
gpio_31=!gpio_31;
}
status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_2);
if (status) {//2来打印此
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_2);
// printf("timer0 comp2 trigger\r\n");
gpio_32=!gpio_32;
}
}
// 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");
gpio = bflb_device_get_by_name("gpio");
//初始化为3个端口
bflb_gpio_init(gpio, GPIO_PIN_30, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
bflb_gpio_init(gpio, GPIO_PIN_31, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
bflb_gpio_init(gpio, GPIO_PIN_32, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
/* 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;//外部时钟为40
cfg0.clock_div = 39; /* for bl616/bl808/bl606p is 39, for bl702 is 31 *///初始的div分频应该是div+1=40
//外部时钟40除以40分频,可获得一个1兆的时钟
cfg0.trigger_comp_id = TEST_TIMER_COMP_ID;
cfg0.comp0_val = 1000000; /* match value 0 *///说明1兆分了10^6,说明comp0进入时候触发的中端为1s
cfg0.comp1_val = 1500000; /* match value 1 *///1.5s
cfg0.comp2_val = 2000000; /* match value 2 *///2.0s
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);
gpio_30 ? bflb_gpio_set(gpio,GPIO_PIN_30) : bflb_gpio_reset(gpio,GPIO_PIN_30);
gpio_31 ? bflb_gpio_set(gpio,GPIO_PIN_31) : bflb_gpio_reset(gpio,GPIO_PIN_31);
gpio_32 ? bflb_gpio_set(gpio,GPIO_PIN_32) : bflb_gpio_reset(gpio,GPIO_PIN_32);
//控制灯光什么时候亮什么时候灭
}
}
通过控制板子的灯光来进行相关数据测试,后续逻辑分析仪回来后会持续更新。。。
总结
嵌入式真的比算法有趣多啦哈哈哈,po上一张学习的图,一起加油!!!