打印
[活动专区]

[活动专区] 【开源活动】- 国民N32G45x的SD卡IAP升级折腾笔记

[复制链接]
435|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 walker2048 于 2023-4-2 19:10 编辑

很高兴能参加本次国民N32G45x的SD卡IAP升级开发活动,通过本次活动,我学到了SDIO的SD卡读写,腾讯TinyOS的fatfs移植等内容。虽然没能完成本次活动作业,但是在折腾的过程中,也学到了不少东西。
以下是本次活动的个人折腾笔记和部分心得。

一、国民技术N32G45x的VSCode开发环境配置
由于我个人非常喜欢用VSCode开发单片机程序,所以只习惯在VSCode下开发。如果单片机不支持VSCode的,那也得想办法弄。经过在网上搜索,学习OpenOCD的芯片移植,把N32G45x的构建系统移植到GN + Ninja上,成功实现了VSCode下的编译和debug调试。以下是运行截图。



移植N32G45x源码到GN + Ninja构建系统上,需要添加以下内容。
  • 添加厂家和单片机的gcc编译配置(build/config/nationstech/BUILD.gn)
  • 添加开发板配置文件和bsp源码文件(hardware/board/n32g45xvl_STB目录下的内容)
  • 添加厂家芯片级SDK文件(hardware/chip/nationstech/n32g457目录下的内容)


单片机的gcc编译配置 build/config/nationstech/BUILD.gn文件内容,此文件定义了编译选项,全局宏定义配置和头文件目录等内容
config("default") {
  defines = []
  cflags = []
  cflags += [
    "-ffunction-sections",
    "-fdata-sections",
    "-fdiagnostics-color=always",
    "-fno-common",
  ]
  cflags += [
    "-Wunused",
    "-Wuninitialized",
  ]
  ldflags = [
    "-nostartfiles",
    "--specs=nosys.specs",
    "-Xlinker",
    "--gc-sections",
  ]
  asmflags = [
    "-g",
    "-x",
    "assembler-with-cpp",
  ]
}

config("n32g457") {
  defines = [
    "N32G45X",
    "USE_STDPERIPH_DRIVER",
  ]
  cflags = [
    "-mcpu=cortex-m4",
    "-mthumb",
    "-mfpu=fpv4-sp-d16",
    "-mfloat-abi=hard",
  ]
  asmflags = cflags
  cflags += [
    "-g",
    "-gdwarf-2",
    "-Os",
  ]
  asmflags += [ "-g" ]
  cflags_c = [ "-std=gnu99" ]
  cflags_cc = [ "-std=c++11" ]
  ldflags = cflags
  ldflags += [
    "-T",
    "../../hardware/chip/${vendor}/${chip}/Link.ld",
  ]
  include_dirs = [
    "//hardware/chip/${vendor}/${chip}/",
    "//hardware/chip/${vendor}/${chip}/algo_lib/inc",
    "//hardware/chip/${vendor}/${chip}/cmsis",
    "//hardware/chip/${vendor}/${chip}/Peripheral/inc",
    "//hardware/chip/${vendor}/${chip}/USBFS/inc",
    "//hardware/board/${product}",
  ]
}

开发板配置文件内容,GN构建生成系统根据此文件确定顶层组件。
hardware/board/n32g45xvl_STB/BUILD.gn文件
# Executable for board   --------------------------------------------------
#
# Executable target configuration.
#
# Setting up configeration fot chip and board.
executable("n32g45xvl_STB") {
  # Common deps for executable target.
  # --------------------------------
  deps = [
    #"//components/net/at",
    ":bsp",
    ":startup",
    "//components/fs/fatfs",
    "//components/fs/kv",
    "//components/fs/vfs",
    "//components/utils",

    #"//applications/${app}",
    "//hardware/arch/arm",
    "//hardware/chip/${vendor}/${chip}:${chip}_sdk",
  ]

  deps += [ "//kernel" ]
}

# Startup sources config.
# --------------------------------
source_set("startup") {
  sources = [ "//hardware/chip/${vendor}/${chip}/startup_n32g45x.s" ]
}

# Startup sources config.
# --------------------------------
source_set("bsp") {
  sources = [
    "main.c",
    "n32g45x_it.c",
  ]
}



hardware/board/n32g45xvl_STB/product.gni文件,GN构建系统根据此文件确定编译工具链,芯片和厂家配置,内核以及app配置
declare_args() {
  # 工具链名称
  # --------------------------------
  compiler = "arm_none_eabi_gcc"

  # gcc,检测工具链用
  # --------------------------------
  gcc = "arm-none-eabi-gcc"

  # 芯片型号
  # --------------------------------
  chip = "n32g457"

  # 厂商
  # --------------------------------
  vendor = "nationstech"

  # 应用
  # --------------------------------
  app = "dummy"

  # RTOS内核
  # --------------------------------
  kernel = "TinyOS"

  # bin类型
  # --------------------------------
  build_type = "debug"

  # ccache缓存
  # --------------------------------
  ccache = true
}

Debug调试配置
调试使用的是cortex-debug插件(只需要安装这个),以下是调试文件配置内容(本项目会根据选择的开发板自行生成该文件)。
.vscode/launch.json
{
    // 使用 IntelliSense 了解相关属性。
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceFolder}/out/n32g45xvl_STB",
            "executable": "./bin/n32g45xvl_STB.elf",
            "device": "n32g457",
            "svdFile": "${workspaceFolder}/hardware/chip/nationstech/n32g457/n32g457.svd",
            "request": "launch",
            "type": "cortex-debug",
            "runToEntryPoint": "main",
            "servertype": "openocd",
            "configFiles": [
                "${workspaceFolder}/hardware/chip/nationstech/debug.cfg"
            ]
        }
    ]
}

配置完以上内容后,还需要编译和安装移植好的OpenOCD,添加hardware/chip/nationstech/debug.cfg文件,检查源码和linkScript文件是否正确匹配,然后就可以开始愉快的用VSCode开发啦。

使用特权

评论回复
沙发
walker2048|  楼主 | 2023-4-2 19:10 | 只看该作者
本帖最后由 walker2048 于 2023-4-2 19:23 编辑

二、移植腾讯TinyOS
1. 线程的栈信息初始化
N32G457使用的是Cortex-M4F内核,而Cortex-M3和Cortex-M4都基于ARMv7-M架构。腾讯TinyOS的ARMv7-M架构支持已经很完善了。这里我们不需要自己去写对应的代码,只需要使用系统里自带的源码即可。
RTOS想从主程序切换到线程上,或者进行线程间的切换,就需要线程的栈信息。然后在准备线程的相关信息时,线程的栈保存是很关键的,如果sp地址没指向正确的地址,就跑飞了。
如果是自己从零开始写,就需要注意port.S文件里,保存寄存器时的顺序,和这个初始化函数里定义的顺序是否一致。
源码的文件路径,hardware/arch/arm/arm-v7m/common/tos_cpu.c
/*----------------------------------------------------------------------------
* Tencent is pleased to support the open source community by making TencentOS
* available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* If you have downloaded a copy of the TencentOS binary from Tencent, please
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
*
* If you have downloaded a copy of the TencentOS source code from Tencent,
* please note that TencentOS source code is licensed under the BSD 3-Clause
* License, except for the third-party components listed below which are
* subject to different license terms. Your integration of TencentOS into your
* own projects may require compliance with the BSD 3-Clause License, as well
* as the other licenses applicable to the third-party components included
* within TencentOS.
*---------------------------------------------------------------------------*/

#include "tos_k.h"

__API__ uint32_t tos_cpu_clz(uint32_t val)
{
#if defined(TOS_CFG_CPU_LEAD_ZEROS_ASM_PRESENT) && (TOS_CFG_CPU_LEAD_ZEROS_ASM_PRESENT == 0u)
    uint32_t nbr_lead_zeros = 0;

    if (!(val & 0XFFFF0000)) {
        val <<= 16;
        nbr_lead_zeros += 16;
    }

    if (!(val & 0XFF000000)) {
        val <<= 8;
        nbr_lead_zeros += 8;
    }

    if (!(val & 0XF0000000)) {
        val <<= 4;
        nbr_lead_zeros += 4;
    }

    if (!(val & 0XC0000000)) {
        val <<= 2;
        nbr_lead_zeros += 2;
    }

    if (!(val & 0X80000000)) {
        nbr_lead_zeros += 1;
    }

    if (!val) {
        nbr_lead_zeros += 1;
    }

    return (nbr_lead_zeros);
#else
    return port_clz(val);
#endif
}

__API__ void tos_cpu_int_disable(void)
{
    port_int_disable();
}

__API__ void tos_cpu_int_enable(void)
{
    port_int_enable();
}

__API__ cpu_cpsr_t tos_cpu_cpsr_save(void)
{
    return port_cpsr_save();
}

__API__ void tos_cpu_cpsr_restore(cpu_cpsr_t cpsr)
{
    port_cpsr_restore(cpsr);
}

__KNL__ void cpu_init(void)
{
    k_cpu_cycle_per_tick = TOS_CFG_CPU_CLOCK / k_cpu_tick_per_second;
    cpu_systick_init(k_cpu_cycle_per_tick);

#if (TOS_CFG_CPU_HRTIMER_EN > 0)
    tos_cpu_hrtimer_init();
#endif
}

__KNL__ void cpu_reset(void)
{
    port_cpu_reset();
}

__KNL__ void cpu_sched_start(void)
{
    port_sched_start();
}

__KNL__ void cpu_context_switch(void)
{
    port_context_switch();
}

__KNL__ void cpu_irq_context_switch(void)
{
    port_irq_context_switch();
}

__KNL__ void cpu_systick_init(k_cycle_t cycle_per_tick)
{
    port_systick_priority_set(TOS_CFG_CPU_SYSTICK_PRIO);
    port_systick_config(cycle_per_tick);
}

#if TOS_CFG_TICKLESS_EN > 0u

/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Set value to systick reload value register
*
* @param cycles The value set to register
*
* [url=home.php?mod=space&uid=266161]@return[/url] None
*/
__STATIC_INLINE__ void cpu_systick_reload(k_cycle_t cycle_per_tick)
{
    port_systick_reload(cycle_per_tick);
}

/**
* @brief Enable systick interrupt
*
* @return None
*/
__KNL__ void cpu_systick_resume(void)
{
    port_systick_resume();
}

/**
* @brief Disable systick interrupt
*
* @return None
*/
__KNL__ void cpu_systick_suspend(void)
{
    port_systick_suspend();
}

__KNL__ k_time_t cpu_systick_max_delay_millisecond(void)
{
    return port_systick_max_delay_millisecond();
}

__KNL__ void cpu_systick_expires_set(k_time_t millisecond)
{
    k_cycle_t cycles;

    cycles = (k_cycle_t)((uint64_t)millisecond * TOS_CFG_CPU_CLOCK / K_TIME_MILLISEC_PER_SEC); /* CLOCK means cycle per second */

    cpu_systick_reload(cycles - 12); /* interrupt delay */
}

__KNL__ void cpu_systick_pending_reset(void)
{
    port_systick_pending_reset();
}

__KNL__ void cpu_systick_reset(void)
{
    cpu_systick_reload(k_cpu_cycle_per_tick);
}

#endif /* TOS_CFG_TICKLESS_EN */

#if TOS_CFG_PWR_MGR_EN > 0u

__KNL__ void cpu_sleep_mode_enter(void)
{
    port_sleep_mode_enter();
}

__KNL__ void cpu_stop_mode_enter(void)
{
    port_stop_mode_enter();
}

__KNL__ void cpu_standby_mode_enter(void)
{
    port_standby_mode_enter();
}

#endif /* TOS_CFG_PWR_MGR_EN */

__KNL__ k_stack_t *cpu_task_stk_init(void *entry,
                                              void *arg,
                                              void *exit,
                                              k_stack_t *stk_base,
                                              size_t stk_size)
{
    cpu_data_t *sp;

    sp = (cpu_data_t *)&stk_base[stk_size];
    sp = (cpu_data_t *)((cpu_addr_t)sp & 0xFFFFFFF8);

#if TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN > 0u
    uint8_t *slot = (uint8_t *)&stk_base[0];
    for (; slot < (uint8_t *)sp; ++slot) {
        *slot = 0xCC;
    }
#endif

    /* auto-saved on exception(pendSV) by hardware */
    *--sp = (cpu_data_t)0x01000000u;    /* xPSR     */
    *--sp = (cpu_data_t)entry;          /* entry    */
    *--sp = (cpu_data_t)exit;           /* R14 (LR) */
    *--sp = (cpu_data_t)0x12121212u;    /* R12      */
    *--sp = (cpu_data_t)0x03030303u;    /* R3       */
    *--sp = (cpu_data_t)0x02020202u;    /* R2       */
    *--sp = (cpu_data_t)0x01010101u;    /* R1       */
    *--sp = (cpu_data_t)arg;            /* R0: arg  */

    /* Remaining registers saved on process stack */
    /* EXC_RETURN = 0xFFFFFFFDL
       Initial state: Thread mode +  non-floating-point state + PSP
       31 - 28 : EXC_RETURN flag, 0xF
       27 -  5 : reserved, 0xFFFFFE
       4       : 1, basic stack frame; 0, extended stack frame
       3       : 1, return to Thread mode; 0, return to Handler mode
       2       : 1, return to PSP; 0, return to MSP
       1       : reserved, 0
       0       : reserved, 1
     */
#if defined (TOS_CFG_CPU_ARM_FPU_EN) && (TOS_CFG_CPU_ARM_FPU_EN == 1U)
    *--sp = (cpu_data_t)0xFFFFFFFDL;
#endif

    *--sp = (cpu_data_t)0x11111111u;    /* R11      */
    *--sp = (cpu_data_t)0x10101010u;    /* R10      */
    *--sp = (cpu_data_t)0x09090909u;    /* R9       */
    *--sp = (cpu_data_t)0x08080808u;    /* R8       */
    *--sp = (cpu_data_t)0x07070707u;    /* R7       */
    *--sp = (cpu_data_t)0x06060606u;    /* R6       */
    *--sp = (cpu_data_t)0x05050505u;    /* R5       */
    *--sp = (cpu_data_t)0x04040404u;    /* R4       */

    return (k_stack_t *)sp;
}

#if TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN > 0u

__KNL__ k_err_t cpu_task_stack_draught_depth(k_stack_t *stk_base, size_t stk_size, int *depth)
{
    uint8_t *slot;
    uint8_t *sp, *bp;
    int the_depth = 0;

    bp = (uint8_t *)&stk_base[0];

    sp = &stk_base[stk_size];
    sp = (uint8_t *)((cpu_addr_t)sp & 0xFFFFFFF8);

    for (slot = sp - 1; slot >= bp; --slot) {
        if (*slot != 0xCC) {
            the_depth = sp - slot;
        }
    }

    *depth = the_depth;
    if (the_depth == stk_size) {
        return K_ERR_TASK_STK_OVERFLOW;
    }

    return K_ERR_NONE;
}

#endif

#if TOS_CFG_FAULT_BACKTRACE_EN > 0u

#if defined (TOS_CFG_CPU_ARM_FPU_EN) && (TOS_CFG_CPU_ARM_FPU_EN == 1U)
__KNL__ void cpu_flush_fpu(void)
{
    (void)__get_FPSCR();
}
#endif

__KNL__ void cpu_fault_diagnosis(void)
{
    port_fault_diagnosis();
}

#endif /* TOS_CFG_FAULT_BACKTRACE_EN */

如果确定这个线程栈信息是正确初始化了,那接下来就可以看线程切换时,保存和恢复现场的相关代码了。
2. 现场保存和恢复相关功能
在初始化了线程的相关内容后,就可以准备开始第一次的线程调度了。然后线程调度的相关汇编代码是在hardware/arch/arm/arm-v7m/cortex-m4/port_s.S这个文件里。
相关的代码也是不需要我们写的,除了下面展示的现场保存和恢复部分的汇编代码以外,开关中断,初次调度线程,唤起调度中断等等内容都是实现好了的。没必要自己去写,有兴趣研究的,可以用一块能运行TinyOS的开发板,在对应的功能上打断点,然后再查看运行前后的mcu寄存器值去学习和了解。
.global port_int_disable
    .global port_int_enable

    .global port_cpsr_save
    .global port_cpsr_restore

    .global port_sched_start
    .global port_context_switch
    .global port_irq_context_switch

    .global port_clz

    .global PendSV_Handler

    .extern k_curr_task
    .extern k_next_task


.equ SCB_VTOR,              0xE000ED08
.equ NVIC_INT_CTRL,         0xE000ED04
.equ NVIC_SYSPRI14,         0xE000ED22
.equ NVIC_PENDSV_PRI,       0xFF
.equ NVIC_PENDSVSET,        0x10000000


   .text
   .align 2
   .thumb
   .syntax unified


.type port_int_disable, %function
port_int_disable:
    CPSID   I
    BX      LR


.type port_int_enable, %function
port_int_enable:
    CPSIE   I
    BX      LR


.type port_cpsr_save, %function
port_cpsr_save:
    MRS     R0, PRIMASK
    CPSID   I
    BX      LR


.type port_cpsr_restore, %function
port_cpsr_restore:
    MSR     PRIMASK, R0
    BX      LR


.type port_clz, %function
port_clz:
    CLZ     R0, R0
    BX      LR


.thumb_func
.type port_sched_start, %function
port_sched_start:
    CPSID   I

    [url=home.php?mod=space&uid=72445]@[/url] set pendsv priority lowest
    @ otherwise trigger pendsv in port_irq_context_switch will cause a context switch in irq
    @ that would be a disaster
    MOVW    R0, #:lower16:NVIC_SYSPRI14
    MOVT    R0, #:upper16:NVIC_SYSPRI14

    MOVW    R1, #:lower16:NVIC_PENDSV_PRI
    MOVT    R1, #:upper16:NVIC_PENDSV_PRI
    STRB    R1, [R0]

    MOVW    R0, #:lower16:SCB_VTOR
    MOVT    R0, #:upper16:SCB_VTOR
    LDR     R0, [R0]
    LDR     R0, [R0]
    MSR     MSP, R0

    MOVW    R0, #:lower16:k_curr_task
    MOVT    R0, #:upper16:k_curr_task

    @ k_curr_task = k_next_task;
    MOVW    R1, #:lower16:k_next_task
    MOVT    R1, #:upper16:k_next_task
    LDR     R2, [R1]
    STR     R2, [R0]

    @ sp = k_next_task->sp
    LDR     R0, [R2]
    @ PSP = sp
    MSR     PSP, R0

    MRS     R0, CONTROL
    ORR     R0, R0, #2
    MSR     CONTROL, R0

    ISB

    @ restore r4-11 from new process stack
    LDMFD    SP!, {R4 - R11}

    #if (defined(__VFP_FP__) && !defined(__SOFTFP__))
    @ ignore EXC_RETURN the first switch
    LDMFD   SP!, {R0}
    #endif

    @ restore r0, r3
    LDMFD    SP!, {R0 - R3}
    @ load R12 and LR
    LDMFD    SP!, {R12, LR}   
    @ load PC and discard xPSR
    LDMFD    SP!, {R1, R2}

    CPSIE    I
    BX       R1


.thumb_func
.type port_context_switch, %function
port_context_switch:
    LDR     R0, =NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR


.thumb_func
.type port_irq_context_switch, %function
port_irq_context_switch:
    LDR     R0, =NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR


.thumb_func
.type PendSV_Handler, %function
PendSV_Handler:
    CPSID   I
    MRS     R0, PSP

_context_save:
    @ R0-R3, R12, LR, PC, xPSR is saved automatically here
    #if (defined(__VFP_FP__) && !defined(__SOFTFP__))
    @ is it extended frame?
    TST     LR, #0x10
    IT      EQ
    VSTMDBEQ  R0!, {S16 - S31}
    @ S0 - S16, FPSCR saved automatically here

    @ save EXC_RETURN
    STMFD   R0!, {LR}
    #endif

    @ save remaining regs r4 - 11 on process stack
    STMFD   R0!, {R4 - R11}

    @ k_curr_task->sp = PSP;
    MOVW    R5, #:lower16:k_curr_task
    MOVT    R5, #:upper16:k_curr_task
    LDR     R6, [R5]
    @ R0 is SP of process being switched out
    STR     R0, [R6]

_context_restore:
    @ k_curr_task = k_next_task;
    MOVW    R1, #:lower16:k_next_task
    MOVT    R1, #:upper16:k_next_task
    LDR     R2, [R1]
    STR     R2, [R5]

    @ R0 = k_next_task->sp
    LDR     R0, [R2]

    @ restore R4 - R11
    LDMFD   R0!, {R4 - R11}

    #if (defined(__VFP_FP__) && !defined(__SOFTFP__))
    @ restore EXC_RETURN
    LDMFD   R0!, {LR}
    @ is it extended frame?
    TST     LR, #0x10
    IT      EQ
    VLDMIAEQ    R0!, {S16 - S31}
    #endif

    @ Load PSP with new process SP
    MSR     PSP, R0
   
    CPSIE   I

    @ R0-R3, R12, LR, PC, xPSR restored automatically here
    @ S0 - S16, FPSCR restored automatically here if FPCA = 1
    BX      LR

.end

3. 系统心跳
在hardware/chip/nationstech/n32g457/cmsis/core_cm4.h文件里,已经实现了通用的系统心跳配置函数,这个也是不需要我们去写的。配置系统心跳的相关内容也不需要我们管,在TinyOS启动内核时会初始化系统心跳。
/* ##################################    SysTick function  ############################################ */
/**
  \ingroup  CMSIS_Core_FunctionInterface
  \defgroup CMSIS_Core_SysTickFunctions SysTick Functions
  \brief    Functions that configure the System.
  @{
*/

#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)

/**
  \brief   System Tick Configuration
  \details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
           Counter is in free running mode to generate periodic interrupts.
  \param [in]  ticks  Number of ticks between two interrupts.
  \return          0  Function succeeded.
  \return          1  Function failed.
  \note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
           function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
           must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

#endif
我们只需要在bsp目录里的hardware/board/n32g45xvl_STB/n32g45x_it.c文件,把SysTick_Handler的实现函数修改一下就可以了。
/**
* @brief  This function handles SysTick Handler.
*/
void SysTick_Handler(void) {
    if (tos_knl_is_running()) {
        tos_knl_irq_enter();
        tos_tick_handler();
        tos_knl_irq_leave();
    }
}
最后,我们在main.c文件(hardware/board/n32g45xvl_STB/main.c)里创建两个调试线程
#define APPLICATION_TASK_STK_SIZE 1024
k_task_t application_task;
__aligned(4) uint8_t application_task_stk[APPLICATION_TASK_STK_SIZE];
k_task_t application_task1;
__aligned(4) uint8_t application_task1_stk[APPLICATION_TASK_STK_SIZE];

void application_entry(void *arg) {
    while (1) {
        tos_task_delay(2500);
        printf("task1 running!\r\n");
    };
}

void application_entry2(void *arg) {
    while (1) {
        tos_task_delay(1200);
        printf("task2 running!\r\n");
    };
}


然后在main函数里创建这两个线程,并开启调度就可以啦


/*********************************************************************
* @fn      main
*
* @brief   Main program.
*
* @return  none
*/
int main(void) {
    bsp_init(); // 初始化debug串口和板级外设

    tos_knl_init(); // 初始化TencentOS tiny内核

    printf("TinyOS demo!\r\n");

    // 创建一个优先级为5的任务
    tos_task_create(&application_task, "task_prio5", application_entry, NULL, 4,
                    application_task_stk, APPLICATION_TASK_STK_SIZE, 0);
    // 创建一个优先级为5的任务
    tos_task_create(&application_task1, "task_prio5", application_entry2, NULL, 4,
                    application_task1_stk, APPLICATION_TASK_STK_SIZE, 0);
    // 开始内核调度,线程中不允许有睡死代码。
    tos_knl_start();

    while (1) {
    }
}


然后就编译、烧录,最后看到串口欢快的跳起线程里的输出内容啦。



使用特权

评论回复
板凳
walker2048|  楼主 | 2023-4-2 19:11 | 只看该作者
本帖最后由 walker2048 于 2023-4-2 20:13 编辑

三、适配腾讯TinyOS的vfs和fatfs组件
1. 由于第一次使用单片机实现SD卡读取,对这方面并不熟悉,所以我只能先使用国民技术官方SDIO的demo来学习。通过demo源码的阅读,可以看出该源码支持SDIO 1bit,4bit,8bit模式(8bit模式应该是适配emmc的)。然后我手上的SD卡模块是spi方式接入,通过对比spi模式和SDIO 1bit模式的引脚,可以使用。以下是接线图。

通过修改源码里传递给SD_Init()函数的第三个参数(由原来的4改为1),即可运行该demo,并对SD卡进行读写测试。测试内容如下。



demo正确获取到了SD卡的信息(除容量获取错误以外),也正确写入和读出了扇区的内容。可以开始移植了。

2. 处理原sdio源码文件
可能是出于兼容性的考虑,腾讯TinyOS的sd卡初始化函数是不传递任何参数的,所以我们需要将原sdio实现代码中的SD_Init函数更名为__SD_Init,并在文件尾重写一个SD_Init函数。以下为改写内容。
SD_Error SD_Init(){
    return __SD_Init(0, 3, 1);
}


3. 实现tos_hal_sd的接口函数
腾讯TinyOS的Fatfs组件里会调用tos_hal_sd层级的函数,避免直接调用芯片级的hal函数,实现多种芯片的软件兼容。所以我们需要为国民N32G457编写对应的文件。
hardware/chip/nationstech/tos_hal/tos_hal_sd.c(文件路径)
#include "tos_k.h"
#include "tos_hal.h"
#include "sdio_tf.h"

__API__ int tos_hal_sd_init(hal_sd_t *sd)
{
    (void)sd;

    return SD_Init() == SD_OK ? 0 : -1;
}

__API__ int tos_hal_sd_read(hal_sd_t *sd, uint8_t *buf, uint32_t blk_addr, uint32_t blk_num, uint32_t timeout)
{
    uint8_t err;

    (void)sd;

    if (!buf) {
        return -1;
    }

    err = SD_ReadDisk(buf, blk_addr, blk_num);
    err = SD_WaitWriteOperation();

    return err == SD_OK ? 0 : -1;
}

__API__ int tos_hal_sd_write(hal_sd_t *sd, const uint8_t *buf, uint32_t blk_addr, uint32_t blk_num, uint32_t timeout)
{
    uint8_t err;

    (void)sd;

    if (!buf) {
        return -1;
    }

    err = SD_WriteDisk((uint8_t *)buf, blk_addr, blk_num);
    err = SD_WaitWriteOperation();
    return err == SD_OK ? 0 : -1;
}

__API__ int tos_hal_sd_read_dma(hal_sd_t *sd, uint8_t *buf, uint32_t blk_addr, uint32_t blk_num)
{
    return -1;
}

__API__ int tos_hal_sd_write_dma(hal_sd_t *sd, const uint8_t *buf, uint32_t blk_addr, uint32_t blk_num)
{
    return -1;
}

__API__ int tos_hal_sd_erase(hal_sd_t *sd, uint32_t blk_add_start, uint32_t blk_addr_end)
{
    return 0;
}

__API__ int tos_hal_sd_info_get(hal_sd_t *sd, hal_sd_info_t *info)
{
    SD_Error err;
    SD_CardInfo card_info;

    (void)sd;

    if (!sd || !info) {
        return -1;
    }

    err = SD_GetCardInfo(&card_info);
    if (err != SD_OK) {
        return -1;
    }

    info->card_type             = card_info.CardType;
    info->blk_num               = card_info.CardCapacity / card_info.CardBlockSize;
    info->blk_size              = card_info.CardBlockSize;

    return 0;
}

__API__ int tos_hal_sd_state_get(hal_sd_t *sd, hal_sd_state_t *state)
{
    int ret = 0;
    SDCardState sd_state;

    (void)sd;

    if (!sd || !state) {
        return -1;
    }

    sd_state = SD_GetState();
    switch (sd_state) {
        case SD_CARD_READY:
            *state = HAL_SD_STAT_READY;
            break;

        case SD_CARD_SENDING:
            *state = HAL_SD_STAT_PROGRAMMING;
            break;

        case SD_CARD_RECEIVING:
            *state = HAL_SD_STAT_RECEIVING;
            break;

        case SD_CARD_TRANSFER:
            *state = HAL_SD_STAT_TRANSFER;
            break;

        case SD_CARD_ERROR:
            *state = HAL_SD_STAT_ERROR;
            break;

        default:
            ret = -1;
            break;
    }
    return ret;
}

__API__ int tos_hal_sd_deinit(hal_sd_t *sd)
{
    (void)sd;
    return 0;
}
4. 将对应文件添加到GN构建配置文件中
添加了新的c源文件,我们需要把对应的文件添加到GN构建配置文件中,每个组件的根目录下都有一个BUILD.gn配置文件。该文件定义了组件包含的文件,组件编译选项(若需要添加),组件依赖等等内容。
由于腾讯TinyOS并没有实现完整的系统级hal层函数,这个tos_hal_sd.c文件就暂时放在厂商芯片代码目录中(hardware/chip/nationstech/),我们在配置文件的最后添加这个文件的配置。
source_set("n32g457_sdk") {
  sources = [
    "Peripheral/misc.c",
    "Peripheral/n32g45x_adc.c",
    "Peripheral/n32g45x_bkp.c",
    "Peripheral/n32g45x_can.c",
    "Peripheral/n32g45x_comp.c",
    "Peripheral/n32g45x_crc.c",
    "Peripheral/n32g45x_dbg.c",
    "Peripheral/n32g45x_dma.c",
    "Peripheral/n32g45x_dvp.c",
    "Peripheral/n32g45x_eth.c",
    "Peripheral/n32g45x_exti.c",
    "Peripheral/n32g45x_flash.c",
    "Peripheral/n32g45x_gpio.c",
    "Peripheral/n32g45x_i2c.c",
    "Peripheral/n32g45x_iwdg.c",
    "Peripheral/n32g45x_opamp.c",
    "Peripheral/n32g45x_pwr.c",
    "Peripheral/n32g45x_qspi.c",
    "Peripheral/n32g45x_rcc.c",
    "Peripheral/n32g45x_rtc.c",
    "Peripheral/n32g45x_sdio.c",
    "Peripheral/n32g45x_spi.c",
    "Peripheral/n32g45x_tim.c",
    "Peripheral/n32g45x_usart.c",
    "Peripheral/n32g45x_wwdg.c",
  ]
  sources += [
    "USBFS/usb_core.c",
    "USBFS/usb_init.c",
    "USBFS/usb_int.c",
    "USBFS/usb_mem.c",
    "USBFS/usb_regs.c",
    "USBFS/usb_sil.c",
  ]
  sources += [
    "sdio_tf.c",
    "system_n32g45x.c",
  ]
  sources += [ "../tos_hal/tos_hal_sd.c" ]
}
适配完毕后,编译源码,默认配置下,TinyOS的fatfs还是比较大的体积的。通过psize命令,可以看到fs/fatfs组件体积又182.3K这么大,如此大的体积,是不符合BootLoader的需求的。


5. 检查代码是否可以读写tf卡
通过修改hardware/board/n32g45xvl_STB/main.c文件,我们尝试初始化SD卡和擦写部分扇区(直接调用SD_WriteBlock函数
/*****************************************************************************
* Copyright (c) 2019, Nations Technologies Inc.
*
* All rights reserved.
* ****************************************************************************
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Nations' name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ****************************************************************************/

/**
* [url=home.php?mod=space&uid=288409]@file[/url] main.c
* [url=home.php?mod=space&uid=187600]@author[/url] Nations
* [url=home.php?mod=space&uid=895143]@version[/url] v1.0.0
*
* [url=home.php?mod=space&uid=17282]@CopyRight[/url] Copyright (c) 2019, Nations Technologies Inc. All rights reserved.
*/
#include "main.h"
#include "n32g45x.h"
#include "sdio_tf.h"
#include "stdint.h"
#include "stdio.h"
#include "tos_k.h"

#define USARTx USART1
#define USARTx_GPIO GPIOA
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define USARTx_RxPin GPIO_PIN_10
#define USARTx_TxPin GPIO_PIN_9

#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(char ch)
#define GETCHAR_PROTOTYPE int __io_getchar()
#else
#define PUTCHAR_PROTOTYPE int fputc(char ch)
#define GETCHAR_PROTOTYPE int fgetc()
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE {
    if (ch == '\n') {
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET)
            ;
        USART_SendData(USARTx, '\r');
    }
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET)
        ;
    USART_SendData(USARTx, ch);
    return ch;
}

GETCHAR_PROTOTYPE {
    uint8_t ch = 0;
    ch = USART_ReceiveData(USARTx);
    return ch;
}

/** @addtogroup N32G45X_StdPeriph_Examples
* @{
*/

/** @addtogroup USART_Printf
* @{
*/

typedef enum { FAILED = 0, PASSED = !FAILED } TestStatus;

#define countof(a) (sizeof(a) / sizeof(*(a)))

USART_InitType USART_InitStructure;

void RCC_Configuration(void);

#define APPLICATION_TASK_STK_SIZE 1024
k_task_t application_task;
__aligned(4) uint8_t application_task_stk[APPLICATION_TASK_STK_SIZE];

u8 buf[512];
u8 Readbuf[512];

/*********************************************************************
* @fn      show_sdcard_info
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   SD Card information.
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
void show_sdcard_info(void) {
    printf("SD Card initialization success!\r\n");
    printf("*CardType            is: %d\r\n", SDCardInfo.CardType);
    printf("*CardCapacity        is: %lld\r\n",
           SDCardInfo.CardCapacity / (1024 * 1024));
    printf("*CardBlockSize       is: %d\r\n", (int)SDCardInfo.CardBlockSize);
    printf("*RCA                 is: %d\r\n", SDCardInfo.RCA);
    printf("*Manufacture(MID)    is: %d\r\n", SDCardInfo.SD_cid.ManufacturerID);
    printf("*OEM/Appli(OID)      is: %d\r\n", SDCardInfo.SD_cid.OEM_AppliID);
    printf("*Product Name(PNM)   is: %d\r\n", (int)SDCardInfo.SD_cid.ProdName1);
    printf("*Serial Number(PSN)  is: %x\r\n",
           (unsigned int)SDCardInfo.SD_cid.ProdSN);
    printf("*Manu Date COde(MDT) is: %x\r\n", SDCardInfo.SD_cid.ManufactDate);
    printf("*Card SysSpecVersion is: %d\r\n", SDCardInfo.SD_csd.SysSpecVersion);
    printf("*Card MaxBusClkFrec  is: %d\r\n", SDCardInfo.SD_csd.MaxBusClkFrec);
    printf("*Card MaxRdBlockLen  is: %d\r\n", SDCardInfo.SD_csd.RdBlockLen);
    printf("*Card RdCurrent VDD  is: %d -> %d\r\n",
           SDCardInfo.SD_csd.MaxRdCurrentVDDMin,
           SDCardInfo.SD_csd.MaxRdCurrentVDDMax);
    printf("*Card WrSpeedFact    is: %d\r\n", SDCardInfo.SD_csd.WrSpeedFact);
    printf("*Card MaxWrBlockLen  is: %d\r\n", SDCardInfo.SD_csd.MaxWrBlockLen);
    printf("*Card WrCurrent VDD  is: %d -> %d\r\n",
           SDCardInfo.SD_csd.MaxWrCurrentVDDMin,
           SDCardInfo.SD_csd.MaxWrCurrentVDDMax);
}

SD_Error Status = SD_OK;

void application_entry(void *arg) {
    u32 i;
    u32 Sector_Nums;

    while (SD_Init()) {
        printf("SD Card Error!\r\n");
        tos_task_delay(1000);
    }

    show_sdcard_info();

    printf("SD Card OK\r\n");
    Sector_Nums = ((u32)(SDCardInfo.CardCapacity >> 20)) / 2;
    printf("Sector_Nums:%d\r\n", Sector_Nums);

    for (i = 0; i < 512; i++) {
        buf[i] = i;
    }

    for (i = 0; i < 512; i++) {
        Status = SD_WriteBlock(buf, i, 1);
        Status = SD_WaitWriteOperation();
        while (SD_GetStatus() != SD_TRANSFER_OK)
            ;
        if (Status != SD_OK) {
            printf("SD Card write block failed!\r\n");
        }
        Status = SD_ReadBlock(Readbuf, i, 1);
        Status = SD_WaitReadOperation();
        while (SD_GetStatus() != SD_TRANSFER_OK)
            ;

        if (memcmp(buf, Readbuf, 512)) {
            printf(" %d sector Verify fail\n", i);
            break;
        }
    }
    if (i == Sector_Nums / 2) {
        printf("SD OK\r\n");
    }
}

/**
* @brief  Main program
*/
int main(void) {
    /* System Clocks Configuration */
    RCC_Configuration();

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

    /* Configure the GPIO ports */
    GPIO_InitType GPIO_InitStructure;

    /* Configure USARTx Tx as alternate function push-pull */
    GPIO_InitStructure.Pin = USARTx_TxPin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);

    /* Configure USARTx Rx as input floating */
    GPIO_InitStructure.Pin = USARTx_RxPin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);

    /* USARTy and USARTz configuration
     * ------------------------------------------------------*/
    USART_InitStructure.BaudRate = 115200;
    USART_InitStructure.WordLength = USART_WL_8B;
    USART_InitStructure.StopBits = USART_STPB_1;
    USART_InitStructure.Parity = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;

    /* Configure USARTx */
    USART_Init(USARTx, &USART_InitStructure);
    /* Enable the USARTx */
    USART_Enable(USARTx, ENABLE);

    tos_knl_init(); // 初始化TencentOS tiny内核

    printf("TinyOS demo!\r\n");

    // 创建一个优先级为5的任务
    tos_task_create(&application_task, "task_prio5", application_entry, NULL, 4,
                    application_task_stk, APPLICATION_TASK_STK_SIZE, 0);
    // 开始内核调度,线程中不允许有睡死代码。
    tos_knl_start();

    while (1) {
    }
}

/**
* @brief  Configures the different system clocks.
*/
void RCC_Configuration(void) {
    /* Enable GPIO clock */
    GPIO_APBxClkCmd(USARTx_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
    /* Enable USARTy and USARTz Clock */
    USART_APBxClkCmd(USARTx_CLK, ENABLE);
}
烧录到开发板上,可以正确获取SD卡信息,并读写SD卡扇区。SDIO代码没问题。

四、fatfs编译优化注意事项

1. 回到上一个步骤的fatfs体积的问题,通过搜索引擎可以查到,我们可以调整FF_USE_LFN 宏定义的值,关闭长文件名的支持。以下为修改后的代码。
文件路径 components/fs/fatfs/wrapper/include/tos_ffconf.h (由于腾讯TinyOS改写了fatfs的源码,并重命名了ffconf.h文件,我们需要修改这个文件才能生效)。
#define FF_USE_LFN                0
#define FF_MAX_LFN                255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() in ffsystem.c, need to be added to the project. */
执行mclean命令清空了编译产物后,重新执行mbuild和psize命令,可以看到fatfs的组件体积减小到了8.9K,体积减小了不少。


虽然整体体积34K的flash,对于16K的目标BootLoader大小来说还是相差比较大。目前就暂时不管他,先考虑完成IAP功能(SDIO功能占用体积极大,估计要换成SPI或者软SPI读写SD卡能降低不少体积)。


这里我们用TinyOS的VFS层代码进行测试,测试代码如下(同样是修改上文提到的main.c文件):
/*****************************************************************************
* Copyright (c) 2019, Nations Technologies Inc.
*
* All rights reserved.
* ****************************************************************************
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Nations' name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ****************************************************************************/

/**
* @file main.c
* @author Nations
* @version v1.0.0
*
* @copyright Copyright (c) 2019, Nations Technologies Inc. All rights reserved.
*/
#include "main.h"
#include "n32g45x.h"
#include "sdio_tf.h"
#include "stdint.h"
#include "stdio.h"
#include "tos_k.h"

#define USARTx USART1
#define USARTx_GPIO GPIOA
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define USARTx_RxPin GPIO_PIN_10
#define USARTx_TxPin GPIO_PIN_9

#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(char ch)
#define GETCHAR_PROTOTYPE int __io_getchar()
#else
#define PUTCHAR_PROTOTYPE int fputc(char ch)
#define GETCHAR_PROTOTYPE int fgetc()
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE {
    if (ch == '\n') {
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET)
            ;
        USART_SendData(USARTx, '\r');
    }
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXC) == RESET)
        ;
    USART_SendData(USARTx, ch);
    return ch;
}

GETCHAR_PROTOTYPE {
    uint8_t ch = 0;
    ch = USART_ReceiveData(USARTx);
    return ch;
}

/** @addtogroup N32G45X_StdPeriph_Examples
* @{
*/

/** @addtogroup USART_Printf
* @{
*/

typedef enum { FAILED = 0, PASSED = !FAILED } TestStatus;

#define countof(a) (sizeof(a) / sizeof(*(a)))

USART_InitType USART_InitStructure;

void RCC_Configuration(void);

#define APPLICATION_TASK_STK_SIZE 1024
k_task_t application_task;
__aligned(4) uint8_t application_task_stk[APPLICATION_TASK_STK_SIZE];

/*********************************************************************
* @fn      show_sdcard_info
*
* @brief   SD Card information.
*
* @return  none
*/
void show_sdcard_info(void) {
    printf("SD Card initialization success!\r\n");
    printf("*CardType            is: %d\r\n", SDCardInfo.CardType);
    printf("*CardCapacity        is: %lld\r\n",
           SDCardInfo.CardCapacity / (1024 * 1024));
    printf("*CardBlockSize       is: %d\r\n", (int)SDCardInfo.CardBlockSize);
    printf("*RCA                 is: %d\r\n", SDCardInfo.RCA);
    printf("*Manufacture(MID)    is: %d\r\n", SDCardInfo.SD_cid.ManufacturerID);
    printf("*OEM/Appli(OID)      is: %d\r\n", SDCardInfo.SD_cid.OEM_AppliID);
    printf("*Product Name(PNM)   is: %d\r\n", (int)SDCardInfo.SD_cid.ProdName1);
    printf("*Serial Number(PSN)  is: %x\r\n",
           (unsigned int)SDCardInfo.SD_cid.ProdSN);
    printf("*Manu Date COde(MDT) is: %x\r\n", SDCardInfo.SD_cid.ManufactDate);
    printf("*Card SysSpecVersion is: %d\r\n", SDCardInfo.SD_csd.SysSpecVersion);
    printf("*Card MaxBusClkFrec  is: %d\r\n", SDCardInfo.SD_csd.MaxBusClkFrec);
    printf("*Card MaxRdBlockLen  is: %d\r\n", SDCardInfo.SD_csd.RdBlockLen);
    printf("*Card RdCurrent VDD  is: %d -> %d\r\n",
           SDCardInfo.SD_csd.MaxRdCurrentVDDMin,
           SDCardInfo.SD_csd.MaxRdCurrentVDDMax);
    printf("*Card WrSpeedFact    is: %d\r\n", SDCardInfo.SD_csd.WrSpeedFact);
    printf("*Card MaxWrBlockLen  is: %d\r\n", SDCardInfo.SD_csd.MaxWrBlockLen);
    printf("*Card WrCurrent VDD  is: %d -> %d\r\n",
           SDCardInfo.SD_csd.MaxWrCurrentVDDMin,
           SDCardInfo.SD_csd.MaxWrCurrentVDDMax);
}

SD_Error Status = SD_OK;

#include "ff.h"
#include "tos_vfs.h"
#include "tos_fatfs_drv.h"
#include "tos_fatfs_vfs.h"

char buf[512];

void application_entry(void *arg) {
    int fd, ret;
    vfs_err_t err;
    extern vfs_blkdev_ops_t sd_dev;
    extern vfs_fs_ops_t fatfs_ops;

    err = tos_vfs_block_device_register("/dev/sd", &sd_dev);
    if (err != VFS_ERR_NONE) {
        printf("/dev/sd block device register failed!\n");
    }

    err = tos_vfs_fs_register("fatfs_sd", &fatfs_ops);
    if (err != VFS_ERR_NONE) {
        printf("fatfs_sd fs register failed!\n");
    }

    if (tos_vfs_fs_mkfs("/dev/sd", "fatfs_sd", FM_FAT32, 0) != 0) {
        printf("mkfs failed!\n");
    }

    if (tos_vfs_fs_mount("/dev/sd", "/fs/fatfs_sd", "fatfs_sd") != 0) {
        printf("mount failed!\n");
    }

    fd = tos_vfs_open("/fs/fatfs_sd/test_file.txt", VFS_OFLAG_CREATE_ALWAYS | VFS_OFLAG_WRITE);
    if (fd < 0) {
        printf("open failed!\n");
    }

    ret = tos_vfs_write(fd, "fatfs sample content", strlen("fatfs sample content"));
    if (ret >= 0) {
        printf("write ok\n");
        printf("write data:\n%s\n", "fatfs sample content");
    } else {
        printf("write error: %d\n", ret);
    }
    ret = tos_vfs_close(fd);
    if (ret < 0) {
        printf("close failed!\n");
    }

    fd = tos_vfs_open("/fs/fatfs_sd/test_file.txt", VFS_OFLAG_EXISTING | VFS_OFLAG_READ);
    if (fd < 0) {
        printf("open file error!\n");
    }

    memset(buf, 0, sizeof(buf));
    ret = tos_vfs_read(fd, buf, sizeof(buf));
    if (ret >= 0) {
        printf("read ok: %d\n", ret);
        printf("read data:\n%s\n", buf);
    } else {
        printf("read error: %d\n", ret);
    }

    ////////////////////////////////////////////////
    ret = tos_vfs_lseek(fd, 2, VFS_SEEK_CUR);
    if (ret < 0) {
        printf("lseek error\n");
    }

    memset(buf, 0, sizeof(buf));
    ret = tos_vfs_read(fd, buf, sizeof(buf));
    if (ret >= 0) {
        printf("read ok: %d\n", ret);
        printf("read data:\n%s\n", buf);
    } else {
        printf("read error: %d\n", ret);
    }
    /////////////////////////////////////////////////

    tos_vfs_close(fd);
}

/**
* @brief  Main program
*/
int main(void) {
    /* System Clocks Configuration */
    RCC_Configuration();

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

    /* Configure the GPIO ports */
    GPIO_InitType GPIO_InitStructure;

    /* Configure USARTx Tx as alternate function push-pull */
    GPIO_InitStructure.Pin = USARTx_TxPin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);

    /* Configure USARTx Rx as input floating */
    GPIO_InitStructure.Pin = USARTx_RxPin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);

    /* USARTy and USARTz configuration
     * ------------------------------------------------------*/
    USART_InitStructure.BaudRate = 115200;
    USART_InitStructure.WordLength = USART_WL_8B;
    USART_InitStructure.StopBits = USART_STPB_1;
    USART_InitStructure.Parity = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;

    /* Configure USARTx */
    USART_Init(USARTx, &USART_InitStructure);
    /* Enable the USARTx */
    USART_Enable(USARTx, ENABLE);

    tos_knl_init(); // 初始化TencentOS tiny内核

    printf("TinyOS demo!\r\n");

    // 创建一个优先级为5的任务
    tos_task_create(&application_task, "task_prio5", application_entry, NULL, 4,
                    application_task_stk, APPLICATION_TASK_STK_SIZE, 0);
    // 开始内核调度,线程中不允许有睡死代码。
    tos_knl_start();

    while (1) {
    }
}

/**
* @brief  Configures the different system clocks.
*/
void RCC_Configuration(void) {
    /* Enable GPIO clock */
    GPIO_APBxClkCmd(USARTx_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
    /* Enable USARTy and USARTz Clock */
    USART_APBxClkCmd(USARTx_CLK, ENABLE);
}


编译代码后,烧录到板子上测试结果如下:



经过调试和阅读相关源码,暂时未找到为何设备注册错误。阅读原厂SDIO源码发现默认是启用了SDIO的DMA模式(似乎也可以通过宏定义不开启DMA模式,禁用后仍是出现上图的报错),但是由于时间仓储,未能继续再往下开发下去。

总结:通过本次活动,我学习到了N32G457的SDIO读写SD卡,以及腾讯TinyOS的相关移植知识。由于本人的能力不足,未能实现本次活动的目标,但也让我学到了不少知识。由于活动时间结束了,本次的分享只能分享部分经验,而不是完整的IAP功能,只能在4月抽空再完成对应功能,并在本帖跟进对应内容。本次活动的相关源码在https://gitee.com/walker2048/mcu_playground/tree/dev/可以获取到。

使用特权

评论回复
地板
jobszheng| | 2023-4-3 11:21 | 只看该作者
楼主还是厉害!
我也一直想折腾TencentOS,时间,精力,更缺乏了一定的勇气。

使用特权

评论回复
5
jobszheng| | 2023-4-3 11:22 | 只看该作者
不过,单从项目本身来说。bootloader的程序还是要首选前后台程序,甚至是无中断的仅状态机程序。

使用特权

评论回复
6
walker2048|  楼主 | 2023-4-3 11:49 | 只看该作者
本来以为TinyOS自带的东西好整一点,结果翻车了。。。
ch32v307上是可以正常用的。继续折腾吧,反正也要学

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

31

帖子

0

粉丝