2349|7

155

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【平头哥RVB2601创意应用开发】使用体验01 -- printf避坑 [复制链接]

  本帖最后由 sonicfirr 于 2022-3-10 13:10 编辑

       在使用RVB2601的过程中,免不了使用到printf()或是SDK提供的LOG输出,再加上本人参与了创意应用开发活动,为了完成比赛项目,逐一功能模块测试,在测试到实时内核任务管理时出现了一个小bug,通过研究源码成功避坑,因而决定写下此篇博文。

1、案例设计

      案例由HelloWorld Demo扩展而来,分析源码了解启动流程。程序是从startup.S开始,到SystemInit()初始化RAM、中断、时钟、DMA、系统节拍、SPIFlash等,再到pre_main(),然后跳转到main()函数。

 

 

 

   

 

        不过,SDK中pre_main()找到了两个,一个在chip_ch2601包,一个在aos包,这里我猜想一个是裸机版入口,一个是RTOS版入口。Demo默认是开启了控制台的,所以RTOS版的可能性最大,实际情况通过“ps命令”查看,也发现的确是有一个“app_task”进程在运行,说明入口函数是aos包中的版本。于是分析任务函数application_task_entry(),它调用了main函数进入了Demo的功能实现代码部分。

 

 

 

2、创建任务

       这里,本人偷点懒,没有去看手册,直接仿照app_task的格式,编写自己的任务,实现逻辑是:新建任务“my_task”,优先级高于启动任务“app_task”,my_task置位标志量flag,然后休眠,app_task运行判断flag置位则做串口输出,然后清零flag。

 

#define MY_TASK_STACK_SIZE 4096		//自建任务堆栈尺寸
#define MY_TASK_APP_PRI    8        //自建任务优先级
static aos_task_t my_task_handle;   //自建任务控制块(这里也不知道术语)
static int flag = 0;                //自建运行标志


static void my_task_entry(void *arg)
{
	
	while(1) {	
		LOGD(TAG, "My Task in running\n");
		flag = 1;
		LOGD(TAG, "flag: %d\n", flag);		
		aos_msleep(5000);
	}
}


int main(void)
{
    board_yoc_init();
    LOGD(TAG, "%s\n", aos_get_app_version());
    oled_init();	
//	test_timer_periodic();          //这里是测试定时器用的(本例无用)
	aos_task_new_ext(&my_task_handle, "my_task", my_task_entry,
                     NULL, MY_TASK_STACK_SIZE, MY_TASK_APP_PRI);

    while (1) {
		if(flag == 1)	{LOGD(TAG, "Hello world! YoC\n"); flag = 0;}
		aos_msleep(1);              //入坑点,没有延时会卡死
    }

    return 0;
}

       所谓坑点,就是在main()函数的while循环中,如果没有加入延时,就会卡死,串口无输出,控制台命令也不起作用了。加了延时,哪怕只有1ms就可以保证运行正常。

 

3、坑点分析

       以本人使用RTOS的经验来猜想,这里很有可能是串口异步操作引起的(其实,当时先以为任务创建有问题,毕竟没有看手册,后来核对无误,才想到这里),于是追踪串口输出的实现过程一路看下去。

       日志输出的宏“LOGD”等,都定义在ulog包的ulog.h中——#define LOG(...) ulog(LOG_ALERT, "AOS", ULOG_TAG, __VA_ARGS__),而ulog()函数则是定义在ulog.c中。函数没有细致分析,两个地方引起了本人注意:一是日志操作前申请了互斥量,二是日志输出也是调用printf实现的。所以本人还是去看printf的实现过程。

       在newlib包的printf.c中,printf函数调用了_vsnprintf函数做格式化,然后使用了“_out_char()”做输出,_out_char()则调用了“_putchar()”,_putchar()调用了“uart_putc()”。

 

//位于newlib\v7.4.3\libc\stdio\printf.c,调用了_out_char() ----- by author
int printf(const char* format, ...)
{
  va_list va;
  va_start(va, format);
  char buffer[1];
  const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
  va_end(va);
  return ret;
}

//位于newlib\v7.4.3\libc\stdio\printf.c,调用了_putchar() ----- by author
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
{
  (void)buffer; (void)idx; (void)maxlen;
  if (character) {
    _putchar(character);
  }
}

//位于newlib\v7.4.3\libc\stdio\printf.c,调用了uart_putc() ----- by author
void _putchar(char character)
{
    uart_putc(character);
}

 

       uart_putc()定义在aos包的console_uart.c中,它调用了“hal_uart_send()”,而hal_uart_send()则是驱动库hal_csi中定义的,看源码也是启动了互斥量。至此,代码追踪差不多了,可见串口且绑定控制台的独一性资源,系统是通过互斥量来申请并使用的。所以本人设计的任务中通过互斥量占用了串口,没有及时释放资源(这里猜想应该是,UART启用了DMA,所以产生了异步操作,任务休眠时,串口还没有来得及输出完毕,互斥量没有得到释放),而加入了延时后,资源得以释放,功能也就正常了。

       看来在多任务使用串口时必须做好串口操作的时长预估,并设计好任务调度流程,以免再出现卡死。

 

//位于aos\v7.4.3\src\devices\console_uart.c中,调用了hal_uart_send() ----- by author
int uart_putc(int ch)
{
    if (g_console_handle == NULL) {
        return -1;
    }

    if (ch == '\n') {
        int data = '\r';
        if (!aos_irq_context()) {
            hal_uart_send(g_console_handle, &data, 1, AOS_WAIT_FOREVER);
        } else {
            hal_uart_send_poll(g_console_handle, &data, 1);
        }
    }

    if (!aos_irq_context()) {
        hal_uart_send(g_console_handle, &ch, 1, AOS_WAIT_FOREVER);
    } else {
        hal_uart_send_poll(g_console_handle, &ch, 1);
    }

    return 0;
}

//位于hal_csi\v7.4.3\csi2\uart.c中,启用了互斥量 ----- by author
int32_t hal_uart_send(uart_dev_t *uart, const void *data, uint32_t size, uint32_t timeout)
{
    int32_t ret = 0;
    unsigned int actl_flags = 0;

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

    aos_mutex_lock(&uart_list[uart->port].tx_mutex, AOS_WAIT_FOREVER);
#ifdef UART_MODE_SYNC
    int32_t num;
    num = csi_uart_send(&uart_list[uart->port].handle, data, size, timeout);

    if (num != size) {
        return -1;
    }
#else
    ret = csi_uart_send_async(&uart_list[uart->port].handle, data, size);

    if (ret < 0) {
        return -1;
    }

    ret = aos_event_get(&uart_list[uart->port].event_write_read, EVENT_WRITE, AOS_EVENT_OR_CLEAR, &actl_flags, timeout);
    if (ret != 0) {
        return -1;
    }
#endif
    aos_mutex_unlock(&uart_list[uart->port].tx_mutex);

    return ret;
}

 

最新回复

https://bbs.eeworld.com.cn/thread-1196869-1-1.html 要手动生成符号   详情 回复 发表于 2022-3-17 13:47
点赞 关注
 
 

回复
举报

7608

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

谢谢分享,得仔细

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

7159

帖子

2

TA的资源

版主

板凳
 

一看就是经常研究底层代码的大佬,这个坑填上了!

 
 
 

回复

308

帖子

0

TA的资源

纯净的硅(初级)

4
 

感谢分享,标记下

 
 
 

回复

153

帖子

0

TA的资源

一粒金砂(中级)

5
 

CDK如何进入某一个函数查看具体内容内容?

点评

https://bbs.eeworld.com.cn/thread-1196869-1-1.html 要手动生成符号  详情 回复 发表于 2022-3-17 13:47
 
 
 

回复

153

帖子

0

TA的资源

一粒金砂(中级)

6
 

YocTools与CDK是什么关系呢?

 
 
 

回复

1142

帖子

24

TA的资源

纯净的硅(高级)

7
 

分析的不错,

 
 
 

回复

1142

帖子

24

TA的资源

纯净的硅(高级)

8
 
梦溪开物 发表于 2022-3-15 09:19 CDK如何进入某一个函数查看具体内容内容?

https://bbs.eeworld.com.cn/thread-1196869-1-1.html

要手动生成符号

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表