AutosarOsMCU多核启动

谈思汽车 2023-10-01 11:45

 智能汽车安全新媒体 



1.1 core0 main 之前MCU 干了什么

1.1.2 链接文件指定入口函数

mcu 启动的地址有很多方式,这里介绍链接文件指定启动位置。使用ENTRY 指定一个symbol (不知道咋翻译)

ENTRY(cstart)


ENTRY 是 编译器给的link文件命令。

The first instruction to execute in a program is called the entry point. You can use the ENTRY
linker script command to set the entry point. The argument is a symbol name:
ENTRY()
There are several ways to set the entry point. The linker will set the entry point by trying each
of the following methods in order, and stopping when one of them succeeds:
▶ the ’-e’ command-line option;
▶ the ENTRY() command in a linker script;
▶ the value of the symbol start , if defined;
▶ the address of the first byte of the . text section, if present;
▶ The address 0.


这里面我们就可以在代码里定义一个函数。函数名字叫做

void cstart(void)

这里面 函数的链接位置,也是通过链接文件来指定。这里不赘述,一般都是”从头开始“。

这里我们要注意一点。cstart 每个core 都可以来调用。我们来看一下cstart 的实现。

void cstart(void)
{
   unsigned int coreID;
   coreID = __MFCR(0xFE1C);

   if(coreID == 0u)
  {
       Ifx_Ssw_jumpToFunction(__StartUpSoftware);
  }
   if(coreID == 1u)
  {
       Ifx_Ssw_jumpToFunction(__Core1_start);
  }
   if(coreID == 2u)
  {
       Ifx_Ssw_jumpToFunction(__Core2_start);
  }
   if(coreID == 3u)
  {
       Ifx_Ssw_jumpToFunction(__Core3_start);
  }
   if(coreID == 4u)
  {
       Ifx_Ssw_jumpToFunction(__Core4_start);
  }
   if(coreID == 5u)
  {
       Ifx_Ssw_jumpToFunction(__Core5_start);
  }
}


1.1.3 __StartUpSoftware

由前面可以知道cstart 是上电后,MCU 指定的第一个函数入口。

第一句有个指令 __MFCR

Move from core register

就是说把core 寄存器 0xFE1C 的数 拿给coreID这个 变量。

这个寄存器是什么呢?

所以当不同的core 来执行 该指令的时候,返回值就是该core的coreID.

到了这里我们开始启动core0。

void __StartUpSoftware(void)

首先这里对A1 寄存器进行了初始化。

Ifx_Ssw_setAddressReg(a1, __SDATA2(0));

实际上就是把SDATA2(0) 数值 set 给 a1 寄存器。那么 SDATA2(0) 是什么呢?

#define __SDATA1(cpu)      __A0_MEM
#define __SDATA2(cpu)     __A1_MEM
#define __SDATA3(cpu)     __A8_MEM
#define __SDATA4(cpu)     __A9_MEM

那么问题来了 __A1_MEM 又是哪里来的呢?

这个又涉及到了链接文件。

    CORE_SEC(.sdata2) : FLAGS(arsl)
  {
      *(.srodata)
      *(.srodata.*)
  } > default_rom
  _SMALL_DATA2_ = SIZEOF(CORE_SEC(.sdata2)) ? ADDR(CORE_SEC(.sdata2)) : (ADDR(CORE_SEC(.sdata2)) & 0xF0000000) + 32k ;
  __A1_MEM = _SMALL_DATA2_;  

这个是什么意思呢?

这段代码是用于计算SMALL_DATA2_的值。首先,它使用SIZEOF(CORE_SEC(.sdata2))来获取.sdata2段的大小,然后判断该大小是否为0。如果不为0,则将ADDR(CORE_SEC(.sdata2))赋值给SMALL_DATA2_;否则,将ADDR(CORE_SEC(.sdata2))与0xF0000000进行按位与运算,再加上32k,最后将结果赋值给SMALL_DATA2_。

所以也就是看有没有内容链接到 这个sec,如果没有,那么就只需要知道 CORE_SEC 的地址就可以了。

这里 default_rom 是 0x8000xxxx, 所以最终计算下来是

0x80008000

也就是说A1 寄存器现在的数值是0x80008000。


下面看一下这些寄存器分别的介绍。

设置完A1 寄存器的global register 地址之后。set 另外一个寄存器。

这里设置固定值

0x00000980u == 1001 1000 0000b

看一下具体含义。

  • Call depth counting is enabled  

  • Write permission to global registers A[0], A[1], A[8], A[9] is enabled.  

  • Supervisor ModeEnables access to all peripheral devices. It enables read/write access to coreregisters and protected peripheral devices. Tasks at this level may disableinterrupts.

什么意思呢?简言之,开启栈深度counter, 开启牛逼权限,开启一些寄存器的读写权限。

有了这些权限之后。判断一下,本次启动是因为软件重启还是硬件复位。不同的reset 方式 对应的启动流程是不一样的。

这里是通过寄存器来判断,具体不同bit 含义如下。不做赘述。

typedef struct _Ifx_SCU_RSTCON_Bits
{
   Ifx_UReg_32Bit ESR0:2;            /**< \brief [1:0] ESR0 Reset Request Trigger Reset Configuration - ESR0 (rw) */
   Ifx_UReg_32Bit ESR1:2;            /**< \brief [3:2] ESR1 Reset Request Trigger Reset Configuration - ESR1 (rw) */
   Ifx_UReg_32Bit reserved_4:2;      /**< \brief [5:4] \internal Reserved */
   Ifx_UReg_32Bit SMU:2;             /**< \brief [7:6] SMU Reset Request Trigger Reset Configuration - SMU (rw) */
   Ifx_UReg_32Bit SW:2;              /**< \brief [9:8] SW Reset Request Trigger Reset Configuration - SW (rw) */
   Ifx_UReg_32Bit STM0:2;            /**< \brief [11:10] STM0 Reset Request Trigger Reset Configuration - STM0 (rw) */
   Ifx_UReg_32Bit STM1:2;            /**< \brief [13:12] STM1 Reset Request Trigger Reset Configuration (If Product has STM1) - STM1 (rw) */
   Ifx_UReg_32Bit STM2:2;            /**< \brief [15:14] STM2 Reset Request Trigger Reset Configuration (If Product has STM2) - STM2 (rw) */
   Ifx_UReg_32Bit STM3:2;            /**< \brief [17:16] STM3 Reset Request Trigger Reset Configuration (If Product has STM3) - STM3 (rw) */
   Ifx_UReg_32Bit STM4:2;            /**< \brief [19:18] STM4 Reset Request Trigger Reset Configuration (If Product has STM4) - STM4 (rw) */
   Ifx_UReg_32Bit STM5:2;            /**< \brief [21:20] STM5 Reset Request Trigger Reset Configuration (If Product has STM5) - STM5 (rw) */
   Ifx_UReg_32Bit reserved_22:10;    /**< \brief [31:22] \internal Reserved */
} Ifx_SCU_RSTCON_Bits;

我们这里说 硬件从零上电的过程。

总结一下:在phase0 主要是初始化core 寄存器,以及赋予一些操作权限。

1.1.4 __StartUpSoftware_Phase2

这里主要对mcu 电源,内部内存进行一些自检。还是要看用户有没有进行配置,进行写自检代码,否则没有实际代码执行。

    /* Power and EVRC configurations */
   IFX_CFG_SSW_CALLOUT_PMS_INIT();

   /* LBIST Tests and evaluation */
   IFX_CFG_SSW_CALLOUT_LBIST();

   /* MONBIST Tests and evaluation */
   IFX_CFG_SSW_CALLOUT_MONBIST();

总结一下:mcu 自检操作。

1.1.5 __StartUpSoftware_Phase3PowerOnResetPath

这里主要是进行上下文初始化。包含 stack 与 CSA。也就是 上下文切换,占空间的 ram 地址。

我们先来分析一下链接文件对stack,CSA 的指定地址。

CORE_ID = CPU0;
SECTIONS
{
   CORE_SEC(.ustack) (LCF_DSPR0_START + LCF_USTACK0_OFFSET):
  { PROVIDE(__USTACK0_END = .);   . = . + LCF_USTACK0_SIZE;    PROVIDE(__USTACK0 = .); }
   
   CORE_SEC(.istack) (LCF_DSPR0_START + LCF_ISTACK0_OFFSET):
  { PROVIDE(__ISTACK0_END = .);   . = . + LCF_ISTACK0_SIZE;    PROVIDE(__ISTACK0 = .); }
   
   CORE_SEC(.csa) (LCF_DSPR0_START + LCF_CSA0_OFFSET):
  { PROVIDE(__CSA0_END = .);   . = . + LCF_CSA0_SIZE;    PROVIDE(__CSA0 = .); }
}

看这个之前先了解一下PROVIDE 是干什么的。简单来说就是通过链接文件来定义了一下symbol,可以给程序使用。程序如果没有定义,就用这里面的定义。具体如下(翻译麻烦,不翻译了。)。

In some cases, it is desirable for a linker script to define a symbol only if it is referenced and
is not defined by any object included in the link. For example, traditional linkers defined the
symbol etext. However, ANSI C requires the user to be able to use etext as a function name
without encountering an error. The PROVIDE keyword may be used to define a symbol, such
as etext, only if it is referenced but not defined. The syntax is PROVIDE(<symbol> = <
expression>).
Here is an example of using PROVIDE to define etext:
SECTIONS
{
.text :
{
(.text)
etext = .;
PROVIDE(etext = .);
}
}
In this example, if the program defines _etext (with a leading underscore), the linker will give
a multiple definition error. If, on the other hand, the program defines etext (with no leading
underscore), the linker will silently use the definition in the program. If the program references
etext but does not define it, the linker will use the definition in the linker script.

在set好A10 寄存器 (也就是SP 指针)后,这里加了一行汇编。

IFX_SSW_INLINE void Ifx_Ssw_DSYNC(void)
{
   __asm__ volatile ("dsync" : : : "memory");
}

什么意思呢:就是保证所有的数据都搞定了,在开始访问下一个数据,也就是说保证做CSA前A10搞好了。

To ensure memory coherency, a prior to any access to an active CSA memory location. DSYNC instruction must be executed  

随之而来初始化CSA。

具体CSA 是什么,这里直接copy一下之前写过的文章。

上下文查看

上下文保存了一些数据寄存器,地址寄存器以及程序状态字和链接字。上下文分为高级context和低级context, 高级context自动保存,低级context需要用户手动保存。这里以高级context为例。看一下怎么查看context使用情况。

连接好调试器,打开CPU 寄存器,这里会有高级context, 低级context以及一起其他的系统寄存器。

芯片系统架构手册可知上下文的数据结构和内容如下图。

我们在调试器打开CPU 寄存器能找到PCXI,这个是链接字。这里以链接字为0x00370B70 为例。

通过取中间可用的信息来计算链接字对应RAM 空间位置。具体计算方式来自英飞凌系统架构手册,比较详细的介绍。实际上就是数据段加上偏移量。填零。

微软自带计算器 计算出 ram位置这里计算出来的是0x7002DC00 是上文我们提到的core0 的 ram空间。文档搬运工。。。

这时候可以使用调试器我这里使用的是Lauterbach可以查看内存信息。dump... 工具搬运工。。。

通过英飞凌系统架构手册里面对context的解析,可以看到上下文使用情况,以及具体数据。需要对每一个寄存器进行调查 可以查看手册。

上下文在任务切换过程中总是保存或者读取。新任务抢占老任务,老任务的上下文会被抢占,新任务结束,继续执行老任务,老任务的上下文会被重读恢复读取使用。后进先出的过程。那么下面看一下在哪些情况下上下文会被操作。

和上面说的一样,高级context保存是系统自动保存。在中断发生,进入trap,函数调用,换句话说 当系统发生需要使用另一个context的时候。高级context就会被自动保存。然而lower context则需要相应的指令去保存。这里提一下,lower 和 upper 对应的地址,对应的链接字,大小格式,都是一样的。比如配置了64个上下文位置,那么这是lower + upper 一共可以用的空间。所以可以通过程序状态字去查看这个context具体指的是lower or upper.

FCX, PCX 这两个分别的 free context List 和 Previous context List 对应的就是用过了的和可用的链接字的位置。那么问题来了,会不会存在用超了的现象,用超了会有措施吗。这里就到提到LCX 这个寄存器,指向的是最后的某个context 所以 当系统认为的 free context  和 LCX 相等时 就需要注意了。Context 马上就要超了。

举个梨子

当前在用的context时CSA2, 那么FCX 指向下一个可用的context就是CSA3, 在CSA3 里面指向的链接字就是CSA4,以此类推 直到LCX和最后一个。手册搬运工。。。

往前的话,上一个使用了的context的链接字就是CSA1.


新建一个context后,前一个用过了的 和 下一个可用的 context都会被更新,但是下一个可用后面的链表还是原来的样子。当CSA3被new出来之后就变成了这样。


自此 SP 指针 与 CSA 都已经被初始化完毕。

执行一次

isync 来确保 指令的操作完整性。

All preceding instructions are executed by the CPU. Then the pipeline is
flushed before the next instruction is executed.

1.1.5 __StartUpSoftware_Phase4

开启内部看门狗,没什么好说的。

&MODULE_SCU.WDTCPU[0]

1.1.6 __StartUpSoftware_Phase5

如果有配置smu, 这里就可以开启SMU 模块,来监控芯片状态。

1.1.7 __StartUpSoftware_Phase6

这里是和用户main 最接近的一次。

这里面执行两个事情。

  • 启动下一个core

    • void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
      {
         /* Set the PC */
         cpu->PC.B.PC = (unsigned int)programCounter >> 1U;

         /* release boot halt mode if required */
         Ifx_CPU_SYSCON syscon;
         syscon = cpu->SYSCON;

         if (syscon.B.BHALT)
        {
             syscon.B.BHALT = 0U;
             cpu->SYSCON    = syscon;
        }

      }
  • 运行至本core的main 函数

    • void __Core0_start(void)

所以这里之后真正的多核开始了并行。一步一步来。

1.2 多核启动

1.2.1 core0 start

  • 重新加载关闭看门狗

  • 通过写寄存器来决定是否开启P/D cache

    /** \brief 920C, CPUx Program Control 0 */
    #define CPU_PCON0 0x920C
    /** \brief 9040, CPUx Data Memory Control Register */
    #define CPU_DCON0 0x9040

然后对其他几个通用寄存器进行初始化。初始化方式和上面类似。这里不一一赘述。

    /* Set A0 Pointer to access global variables with small data addressing */
   Ifx_Ssw_setAddressReg(a0, __SDATA1(0));

   /* These to be un commented if A8 and A9 are required to be initialized */
   Ifx_Ssw_setAddressReg(a8, __SDATA3(0));
   Ifx_Ssw_setAddressReg(a9, __SDATA4(0));

随之而来的就是 初始化中断向量表。注意这里中断向量表可以说是每个core都可以存在一个。互不干扰。

Os_InitializeVectorTable();

这个中断向量表是配置在os 里面的中断。里面包含中断服务函数,与优先级。在os 的实现代码里面是汇编。部分如下。详细可查看文件:Os_vectors.c

__asm__("\n\
 .file \"Os_vectors.s\"\n\
 .section \".os_interrupt_code.osinterrupts\", \"ax\", @progbits\n\
 #==========================================\n\
 # Os_InterruptVectorTable0\n\
 #==========================================\n\
 .align 13\n\
 .global Os_InterruptVectorTable0 ;# Start of the table\n\
Os_InterruptVectorTable0:\n\

初始化了中断向量表,后面就需要立即给中断进行栈分配。

Ifx_Ssw_MTCR(CPU_ISP, (unsigned int)__ISTACK(0));

注意这里中断有自己独立的栈空间。和前面任务栈一个意思。

由于看门狗是自动起的。下面需要进行ram 初始化,这里先关闭看门狗,如下操作。

    Ifx_Ssw_setCpuEndinitInline(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);

   /* CPU and safety watchdogs are enabled by default,
    * C initialization functions are not servicing the watchdogs.
    */
   Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);
   Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);

前面已经对中断,对栈空间,对通用寄存器进行了初始化与配置。

下面开始对ram 进行初始化。ram初始化主要有两个方面。一个是从对应的falsh 拿数值放到ram 里面。

一个是初始化直接是0. 那么怎么,哪里,如何从flash 里拿到对应的ram呢?CPU 怎么知道的。

前面有个文章已经进行说明。也是通过链接文件的方式。copytabble 方式。

这里给出接口函数。

IFX_SSW_INLINE void Ifx_Ssw_C_InitInline(void)
{
   Ifx_Ssw_CTablePtr pBlockDest, pBlockSrc;
   unsigned int      uiLength, uiCnt;
   unsigned int     *pTable;
   /* clear table */
   pTable = (unsigned int *)&__clear_table;

   while (pTable)
  {
       pBlockDest.uiPtr = (unsigned int *)*pTable++;
       uiLength         = *pTable++;

       /* we are finished when length == -1 */
       if (uiLength == 0xFFFFFFFF)
      {
           break;
      }

       uiCnt = uiLength / 8;

       while (uiCnt--)
      {
           *pBlockDest.ullPtr++ = 0;
      }

       if (uiLength & 0x4)
      {
           *pBlockDest.uiPtr++ = 0;
      }

       if (uiLength & 0x2)
      {
           *pBlockDest.usPtr++ = 0;
      }

       if (uiLength & 0x1)
      {
           *pBlockDest.ucPtr = 0;
      }
  }

   /* copy table */
   pTable = (unsigned int *)&__copy_table;

   while (pTable)
  {
       pBlockSrc.uiPtr  = (unsigned int *)*pTable++;
       pBlockDest.uiPtr = (unsigned int *)*pTable++;
       uiLength         = *pTable++;

       /* we are finished when length == -1 */
       if (uiLength == 0xFFFFFFFF)
      {
           break;
      }

       uiCnt = uiLength / 8;

       while (uiCnt--)
      {
           *pBlockDest.ullPtr++ = *pBlockSrc.ullPtr++;
      }

       if (uiLength & 0x4)
      {
           *pBlockDest.uiPtr++ = *pBlockSrc.uiPtr++;
      }

       if (uiLength & 0x2)
      {
           *pBlockDest.usPtr++ = *pBlockSrc.usPtr++;
      }

       if (uiLength & 0x1)
      {
           *pBlockDest.ucPtr = *pBlockSrc.ucPtr;
      }
  }
}

ram已经初始化完毕。可以开启看门狗。

Ifx_Ssw_enableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);

终于到了core0 的main 函数。

void core0_main (void)

好这里我们等一下,前面说到,这个函数做了两个事情,一个是运行至core0的main. 还有一个是 __Core1_start。

对了现在两个核独立的跑了,我们需要同步分析一下另外一个core.

1.2.2 __Core1_start

首先我们看一下start core 的接口函数什么样的。

void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
{
   /* Set the PC */
   cpu->PC.B.PC = (unsigned int)programCounter >> 1U;

   /* release boot halt mode if required */
   Ifx_CPU_SYSCON syscon;
   syscon = cpu->SYSCON;

   if (syscon.B.BHALT)
  {
       syscon.B.BHALT = 0U;
       cpu->SYSCON    = syscon;
  }

}

其实就是把对应的core的 寄存器 置为,进而让核运行起来。然后第二个参数,就是即将运行的函数。

Ifx_Strict_32Bit BHALT:1;         /**< \brief [24:24] Boot Halt - BHALT (rw) */

分析一下

void __Core1_start(void)

这里与前面分析的Core0 一致。只是不需要对全局ram进行初始化。因为前面已经初始化了。

还有不同的点就是这里可能会拉起后面的core. 如core2.

然后运行至自己的main函数。即:

void core1_main (void)
{
 volatile unsigned short LoopFlag = 1U;
 unsigned short cpuWdtPassword;
 #if ((defined IFX_CFG_SSW_ENABLE_TRICORE0) && (IFX_CFG_SSW_ENABLE_TRICORE0 == 0))
 unsigned short safetyWdtPassword;
 #endif
 ENABLE();
 /*
  * !!WATCHDOG1 IS DISABLED HERE!!
  * Enable the watchdog in the demo if it is required and also service the watchdog periodically
  * */

 #if ((defined IFX_CFG_SSW_ENABLE_TRICORE0) && (IFX_CFG_SSW_ENABLE_TRICORE0 == 0))
 safetyWdtPassword = Ifx_Ssw_getSafetyWatchdogPassword();
 Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);
 #endif

 cpuWdtPassword = Ifx_Ssw_getCpuWatchdogPassword(&MODULE_SCU.WDTCPU[1]);
 Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[1], cpuWdtPassword);

 main();

 while(LoopFlag == 1U)
{

}
}

所以从上面也能看出来,多核的启动是一个拉着一个的,

不是说core0 直接把所有都拉起来的。

后面的每一个core 怎么拉起来, 这里就不介绍了,和core1被拉起来的方式一致。

那么也就是说,我们说到了 两个main 函数。

core0 和 core1 都已经运行到了自己的main 函数。这里就涉及到同步的概念了,先期的core 要等一会 暂时还没有启动的core. 是什么意思呢?我们继续说。

1.3 各自main函数

1.3.1 core0 的main 函数

extern int main(void);
void core0_main (void)
{
 volatile unsigned short LoopFlag = 1U;
 unsigned short cpuWdtPassword;
 unsigned short safetyWdtPassword;


 ENABLE();
 /*
  * !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
  * Enable the watchdog in the demo if it is required and also service the watchdog periodically
  * */
 cpuWdtPassword = Ifx_Ssw_getCpuWatchdogPassword(&MODULE_SCU.WDTCPU[0]);
 safetyWdtPassword = Ifx_Ssw_getSafetyWatchdogPassword();
 Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);
 Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);

 main();

 while (LoopFlag == 1U)
{

}
}

不难看出基操后面跟了个main() 然后就是while 死循环了。由此猜想。走完这个main. 可能就被os 接管了。换句话说,分析到这里,基本和autosar 没有一毛钱关系,除了,前面的中断向量表。不过那个中断其实mcal也可以实现。只是为了配合os 所以才有的。好 现在我们来分析这个

int main(void)

这实际是个宏展开。我来给大家展开一下就一目了然了。

extern int main(void);

int main(void)
{
(
  {
     unsigned newval = (0x200 | (({ unsigned res; __asm__ volatile ("mfcr %0," "0xfe04" : "=d" (res) : : "memory"); res; })));
      __asm__ volatile ("mtcr " "0xfe04" ",%0" : : "d" (newval) : "memory");
  }
);
 
 __asm__ volatile ("isync" : : : "memory");
     
   Os_StartCoreGate();
   
   inner_main();
   
   return 0;
     
}
     
void inner_main(void)
{
   Dem_SetOperationCycleState(0u, 0);
   /*Invoking the ECUM Init for ECU Initialization, never return */
   EcuM_Init();
}

这里也看出在EcuM_Init() 的时候,才真正运行到Autosar 协议栈。

好,那我们看看前面是什么。

有一个函数:

FUNC(void, OS_CODE) Os_StartCoreGate(void) {
 uint32 core = OS_MFCR(0xfe1c);

 /* Prevent secondary cores starting before given permission in Os_Cbk_StartCore() */
 while ((core == 1U) && (Os_StartBlock[0] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 2U) && (Os_StartBlock[1] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 3U) && (Os_StartBlock[2] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 4U) && (Os_StartBlock[3] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 5U) && (Os_StartBlock[4] != 0xa55a5aa5U)) {OS_NOP();}
}

这是在干啥呢。哦。这里貌似在同步每一个core. 好不着急,我们继续往下看。

void inner_main(void)
{
   Dem_SetOperationCycleState(0u, 0);
   /*Invoking the ECUM Init for ECU Initialization, never return */
   EcuM_Init();
}

暂不介绍Dem 模块,这里进行了EcuM_Init().

当然这里会对mcu外设,等寄存器进行初始化。这里我们先不说,后面有专门的文章来介绍EcuM的相关说明。我们只说EcuM 是怎么拉起来Os的。

在EcuM 初始化list0,list1 之后,会调用

EcuM_Prv_StartSlaveCores();
void EcuM_Prv_StartSlaveCores( void )
{
/*local variables*/
   StatusType dataStatus_chr = E_NOT_OK;
   uint16 cntrLoopCtr_u16;
/*Starting all the OS Cores in a loop*/


       for( cntrLoopCtr_u16=0; cntrLoopCtr_u16<ECUM_CFG_NUM_OS_CORES ; cntrLoopCtr_u16++ )
      {
           StartCore( cntrLoopCtr_u16, &dataStatus_chr);
           if(dataStatus_chr != E_OK)
          {
               /* StartCore Failed*/

               EcuM_ErrorHook(ECUM_E_START_CORE_FAILED);

          }
      }

}

也就是说会对后面的每一个core进行

StartCore( cntrLoopCtr_u16, &dataStatus_chr);

这里就是Autosar Os对应的start core.

FUNC(void, OS_CODE) Os_StartCore(CoreIdType CoreID, Os_StatusRefType Status) {
 *Status = E_OK; /* [$UKS 1628] */
 if (CoreID >= 3U) {
   *Status = E_OS_ID; /* [$UKS 1629] */
} else if (*Os_const_coreconfiguration[CoreID].state > 1U) {
   *Status = E_OS_ACCESS; /* [$UKS 1631] */
} else if (*Os_const_coreconfiguration[CoreID].state != 0U) {
   *Status = E_OS_STATE; /* [$UKS 1632] [$UKS 1633] */
} else {
   /* OK */
}
 if (*Status == E_OK) {
   /* [$UKS 1634] */
  (*Os_const_coreconfiguration[CoreID].state) = 1U;  /* Started */
   if ((CoreIdType)OS_MFCR(0xfe1c) != CoreID) {
     *Status = Os_Cbk_StartCore(CoreID);
  }
}
 return;
} /* StartCore */

这里可以发现,在起core的时候有callback函数。那这个对应的是什么呢。

FUNC(StatusType, OS_CALLOUT_CODE) Os_Cbk_StartCore(uint16 CoreID) {
 StatusType ret = E_OS_ID;

 if (CoreID == 1U) {
   Os_StartBlock[0] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU1_PC.U = (uint32)cstart;
   CPU1_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 2U) {
   Os_StartBlock[1] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU2_PC.U = (uint32)cstart;
   CPU2_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 3U) {
   Os_StartBlock[2] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU3_PC.U = (uint32)cstart;
   CPU3_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 4U) {
   Os_StartBlock[3] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU4_PC.U = (uint32)cstart;
   CPU4_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 5U) {
   Os_StartBlock[4] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU5_PC.U = (uint32)cstart;
   CPU5_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;
} else {
   /* Not an expected core! */
}
 return ret;
}

这里是不是豁然开朗,其实core本身已经启动了。这里os 同步一下。给每一个Os_StartBlock[] 进行赋值。

也就是说当core0 走了EcuM_Init 到这里之后,才可以说 来识别每一个core 同步。core0 来给每一个core进行赋值。

这样各自core 就不会卡在 下面函数里了。

FUNC(void, OS_CODE) Os_StartCoreGate(void) {
 uint32 core = OS_MFCR(0xfe1c);

 /* Prevent secondary cores starting before given permission in Os_Cbk_StartCore() */
 while ((core == 1U) && (Os_StartBlock[0] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 2U) && (Os_StartBlock[1] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 3U) && (Os_StartBlock[2] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 4U) && (Os_StartBlock[3] != 0xa55a5aa5U)) {OS_NOP();}
 while ((core == 5U) && (Os_StartBlock[4] != 0xa55a5aa5U)) {OS_NOP();}
}

当每一个core同步之后。除了core0 其他的core都跑到各自的

inner_main();

因为那些core暂时没有配置EcuM 所以就直接到了while(1)

也就是说,等着os 来进行调度。

core0 这时候也进入了

EcuM_Prv_StartOS();

即:

StartOS(x) 
{Os_StackBase[OS_MFCR(0xfe1c)] = Os_GetSP();
if (Os_StartOS(x)) {while(Os_Cbk_Idle()) {} /* [$UKS 161] */;
for(;;)
{} /* [$UKS 16] */
}
}

到这里为止 多核系统启动完毕。从裸机让Autosar Os 进行接管。


-  THE END  -

因文章部分文字及图片涉及到引用,如有侵权,请及时联系17316577586,我们将删除内容以保证您的权益。

谈思汽车 智能汽车安全新媒体
评论 (0)
  • 13、如何解决直插差模电感的异响问题
    13、如何解决直插差模电感的异响问题
  • 基本_碳化硅功率器件_选型手册_2023Q3.pdf
    基本_碳化硅功率器件_选型手册_2023Q3.pdf
  • [完结19章]SpringBoot开发双11商品服务系统
    今天给大家分享一下关于SpringBoot开发双11商品服务系统的整个流程,我将深度还原大厂实习期技术成长全流程,让你收获大厂项目开发全流程与实战经验,具备应对大流量场景问题的解决能力,全面助力提升实习/转正/跳槽表现力与成功率。


    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

    目的
    让大家更容易使用 spring,更容易集成各种常用的中间件、开源软件。
    SpringBoot 基于 Spring 开发, SpringBoot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。
    SpringBoot 不是用来替代 spring 的解决方案,而是和 spring 框架紧密结合提升 spring 开发者体验的工具。

    准备测试数据
    我们先导入准备好的测试数据,这个测试数据是一份商品数据。

    字段包含商品id,name(商品名)
    last_month_sales(最近一个月的销量)
    favorites(收藏数)这几个字段,我们主要是通过商品名来搜索。
    首先我先先创建一个商品索引
    PUT goods
    {
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
      },
      "mappings": {
        "properties": {
          "id": {
            "type": "keyword",
            "doc_values": false,
            "norms": false,
            "similarity": "boolean"
          },
          "name": {
            "type": "text"
          },
            "price": {
            "type": "double"
          },
          "last_month_sales": {
            "type": "long"
          },
          "favorites": {
            "type": "long"
          },
          "year":{
            "type": "short"
          }
        }
      }
    }
    千里之行,始于足下。想要舒舒服服地使用Spring框架,就要把它的开发环境配置好,这对它好,也对我好。

    1. jdk 的配置       
    使用 IDEA 进行开发,在 IDEA 中配置 jdk 的方式很简单,打开 File->Project Structure选择 SDKs。
    在 JDK home path 中选择本地 jdk 的安装目录。
    在 Name 中为 jdk 自定义名字通过以上三步骤,即可导入本地安装的 jdk。如果是使用 STS 或者 eclipse 可以通过两步骤添加:
    window->preference->java->Instralled JRES 来添加本地 jdk。
    window-->preference-->java-->Compiler 选择 jre,和 jdk 保持一致。
    PUT test_index/_doc/1
    {
      "string_field":"imooc",
      "int_field": 100,
      "float_field":3.14,
      "bool_field":true,
      "date_field":"2022/03/16",
      "obj_field":{"key1":"value1","key2":100},
      "array_field1":[100,3.14],
      "array_field2":[100,"200"],
      "array_field3":["2022/03/16","100"],
      "array_field4":["100","2022/03/16"],
      "null_field":null
      }
      创建 Spring Boot 项目后需要进行 maven 配置。打开 File->settings,搜索 maven,配置一下本地的 maven 信息。在 Maven home directory 中选择本地 Maven 的安装路径;在 User settings file 中选择本地 Maven 的配置文件所在路径。在配置文件中配置一下国内阿里的镜像,这样在下载 maven 依赖时,速度会变得很快。
    {
      "test_index" : {
        "mappings" : {
          "properties" : {
            "array_field" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "bool_field" : {
              "type" : "boolean"
            },
            "date_field" : {
              "type" : "date",
              "format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
            },
            "float_field" : {
              "type" : "float"
            },
            "int_field" : {
              "type" : "long"
            },
            "obj_field" : {
              "properties" : {
                "key1" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                },
                "key2" : {
                  "type" : "long"
                }
              }
            },
            "string_field" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        }
      }
    }

    从以上结果中,我们可以看到 Spring Boot 通过MVN方式自动为项目配置了对应的 springframework、logging、jackson 以及 Tomcat 等依赖,而这些正是我们在开发 Web 项目时所需要的。

    那么细心的同学可能会发现一个问题,即在以上 pom.xml 的配置中,引入依赖 spring-boot-starter-web 时,并没有指明其版本(version),但在依赖列表中,我们却看到所有的依赖都具有版本信息,那么这些版本信息是在哪里控制的呢? 
    {
      "_index" : "test_index",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 0,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "string_field" : "Chan",
        "int_field" : 100,
        "int_string_field" : "100",
        "float_field" : 3.14,
        "bool_field" : true,
        "date_field" : "2022/03/16",
        "obj_field" : {
          "key1" : "value1",
          "key2" : 100
        },
        "array_field" : [
          "value1",
          "100"
        ],
        "null_field" : null
      }
    }
    spring-boot-starter-parent 是所有 Spring Boot 项目的父级依赖,它被称为 Spring Boot 的版本管理中心,可以对项目内的部分常用依赖进行统一管理。

    <parent>    

            <groupId>org.springframework.boot</groupId>    

            <artifactId>spring-boot-starter-parent</artifactId>    

            <version>2.5.6</version>    

            <relativePath/> 

    </parent>

    Spring Boot 项目可以通过继承 spring-boot-starter-parent 来获得一些缺省的配置内容,它主要提供了以下特性:

    默认 JDK 版本(Java 8)

    默认字符集(UTF-8)

    依赖管理功能

    资源过滤

    默认插件配置识别 

    application.properties 或 application.yml 类型的配置文件
    DELETE test_index

    PUT test_index
    {
      "mappings": {
        "dynamic":false 
      }
    }
    GET test_index/_search
    {
      "query": {
       "term": {
         "field1.field2": {
           "value": "imooc ES"
         }
       }
      }
    }

    GET test_index/_doc/4

    DELETE test_index

    PUT test_index
    {
      "mappings": {
        "dynamic":"strict" 
      }
    }


    POST test_index/_doc/2
    {
      "field1":{
       "field2":"imooc ES" 
      }
    }

    GET test_index/_search
    {
      "query": {
       "term": {
         "field1.field2": {
           "value": "imooc ES"
         }
       }
      }
    }

    GET test_index/_doc/4

    以下就是本文的全部内容,感谢大家观看
  • [完结19章]SpringBoot开发双11商品服务系统教程下载
    如何使用SpringBoot开发一款关于双11商品服务的系统?今天就给大家说道说道,希望对大家的学习有所帮助!
    1.什么是SpringBoot?
    Spring 的诞⽣是为了简化 Java 程序的开发的,⽽ Spring Boot 的诞⽣是为了简化 Spring 程序开发的。
    Spring Boot是由Pivotal团队提供的基于Spring的框架,该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。Spring Boot集成了绝大部分目前流行的开发框架,就像Maven集成了所有的JAR包一样,Spring Boot集成了几乎所有的框架,使得开发者能快速搭建Spring项目。
    2.SpringBoot的优点
    快速集成框架,Spring Boot 提供了启动添加依赖的功能,⽤于秒级集成各种框架。
    内置运⾏容器,⽆需配置 Tomcat 等 Web 容器,直接运⾏和部署程序。
    快速部署项⽬,⽆需外部容器即可启动并运⾏项⽬。
    可以完全抛弃繁琐的 XML,使⽤注解和配置的⽅式进⾏开发。
    ⽀持更多的监控的指标,可以更好的了解项⽬的运⾏情况

    后端配置
    1.1创建Springboot工程
    打开idea->file->new->project
    选择spring Initializer进行配置,java版本选择8,点击next
    - internal 应用代码
        - controllers 控制器模块
          - admin 后端控制器
          - front 前端控制器
        - listen redis监听器
        - models 模型模块
        - service 服务模块
          - product_serive 商品服务
          - wechat_menu_serive 微信公众号菜单服务
          ......
    - conf 公共配置
      -config.yml yml配置文件
      -config.go 配置解析,转化成对应的结构体
      
    - middleware 中间件
        - AuthCheck.go  jwt接口权限校验
    - cors.go 跨域处理
    ......
    - pkg 程序应用包
      - app
      - base
      - casbin
      - jwt
      - qrcode
      - wechat
      .....
    - routere 路由
    - logs 日志存放
    - runtime 资源目录
    首先,我仔细分析了需求,并且根据业务逻辑设计了合适的接口。
    对于多表关联查询,我使用了MyBatis的注解来编写SQL语句,并通过@One和@Many等注解来实现结果集的映射。
    对于数据分页,我使用了MyBatis-Plus提供的Page对象,并结合相关方法来实现数据分页查询。
    2. 上课中的优秀项目
    在课堂上,我完成了一个优秀的项目,主要是学生实体类的增删改查功能。通过这个项目,我巩固了对Spring Boot框架的理解和掌握。
    具体实现如下:
    //初始化redis
    err := cache.InitRedis(cache.DefaultRedisClient, &redis.Options{
    Addr:        global.CONFIG.Redis.Host,
    Password:    global.CONFIG.Redis.Password,
    IdleTimeout: global.CONFIG.Redis.IdleTimeout,
    }, nil)
    if err != nil {
    if err != nil {
    global.LOG.Error("InitRedis error ", err, "client", cache.DefaultRedisClient)
    panic(err)
    }
    panic(err)
    }

    //初始化mysql
    err = db.InitMysqlClient(db.DefaultClient, global.CONFIG.Database.User,
    global.CONFIG.Database.Password, global.CONFIG.Database.Host,
    global.CONFIG.Database.Name)
    if err != nil {
    global.LOG.Error("InitMysqlClient error ", err, "client", db.DefaultClient)
    panic(err)
    }
    global.Db = db.GetMysqlClient(db.DefaultClient).DB

    开发步骤
    SpringBoot 开发起来特别简单,分为如下几步:
    创建新模块,选择Spring初始化,并配置模块相关基础信息
    选择当前模块需要使用的技术集
    开发控制器类
    运行自动生成的Application类
    知道了 SpringBoot 的开发步骤后,接下来我们进行具体的操作
    shutdown.NewHook().Close(
    //关闭http server
    func() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    if err := server.Shutdown(ctx); err != nil {
    logging.Error("http server shutdown err", err)
    }
    },

    func() {
    //关闭kafka producer(特别是异步生产者,强制关闭会导致丢消息)
    if err := mq.GetKafkaSyncProducer(mq.DefaultKafkaSyncProducer).Close(); err != nil {
    logging.Error("kafka shutdown err", err)
    }
    },
    func() {
    //关闭mysql
    if err := db.CloseMysqlClient(db.DefaultClient); err != nil {
    logging.Error("mysql shutdown err", err)
    }
    },
    func() {
    //关闭redis
    if err := cache.GetRedisClient(cache.DefaultRedisClient).Close(); err != nil {
    logging.Error("redis shutdown err", err)
    }
    },
    )
    //也可以自己实现优雅关闭
    //signals := make(chan os.Signal, 0)
    //signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    //s := <-signals
    //global.LOG.Warn("shop receive system signal:", s)
    //ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    //defer cancel()
    //err := server.Shutdown(ctx)
    //if err != nil {
    // global.LOG.Error("http server error", err)
    //}
    //mq.GetKafkaSyncProducer(mq.DefaultKafkaSyncProducer).Close()

    选择 Spring Initializr ,用来创建 SpringBoot 工程
    以前我们选择的是 Maven ,今天选择 Spring Initializr 来快速构建 SpringBoot 工程。而在 Module SDK 这一项选择我们安装的 JDK 版本。
    type StoreProduct struct {
    Image        string         `json:"image" valid:"Required;"`
    SliderImage  string         `json:"slider_image" valid:"Required;"`
    StoreName    string         `json:"store_name" valid:"Required;"`
    StoreInfo    string         `json:"store_info" valid:"Required;"`
    Keyword      string         `json:"keyword" valid:"Required;"`
    CateId       int            `json:"cate_id" valid:"Required;"`
    ProductCate  *StoreCategory `json:"product_cate" gorm:"foreignKey:CateId;association_autoupdate:false;association_autocreate:false"`
    Price        float64        `json:"price" valid:"Required;"`
    VipPrice     float64        `json:"vip_price" valid:"Required;"`
    OtPrice      float64        `json:"ot_price" valid:"Required;"`
    Postage      float64        `json:"postage" valid:"Required;"`
    UnitName     string         `json:"unit_name" valid:"Required;"`
    Sort         int16          `json:"sort" valid:"Required;"`
    Sales        int            `json:"sales" valid:"Required;"`
    Stock        int            `json:"stock" valid:"Required;"`
    IsShow       *int8          `json:"is_show" valid:"Required;"`
    IsHot        *int8          `json:"is_hot" valid:"Required;"`
    IsBenefit    *int8          `json:"is_benefit" valid:"Required;"`
    IsBest       *int8          `json:"is_best" valid:"Required;"`
    IsNew        *int8          `json:"is_new" valid:"Required;"`
    Description  string         `json:"description" valid:"Required;"`
    IsPostage    *int8          `json:"is_postage" valid:"Required;"`
    GiveIntegral int            `json:"give_integral" valid:"Required;"`
    Cost         float64        `json:"cost" valid:"Required;"`
    IsGood       *int8          `json:"is_good" valid:"Required;"`
    Ficti        int            `json:"ficti" valid:"Required;"`
    Browse       int            `json:"browse" valid:"Required;"`
    IsSub        *int8          `json:"is_sub" valid:"Required;"`
    TempId       int64          `json:"temp_id" valid:"Required;"`
    SpecType     int8           `json:"spec_type" valid:"Required;"`
    IsIntegral   *int8          `json:"isIntegral" valid:"Required;"`
    Integral     int32          `json:"integral" valid:"Required;"`
    BaseModel
    }

    //定义商品消息结构
    type ProductMsg struct {
    Operation string `json:"operation"`
    *StoreProduct
    }
    切换web服务器
    现在我们启动工程使用的是 tomcat 服务器,那能不能不使用 tomcat 而使用 jetty 服务器,jetty 在我们 maven 高级时讲 maven 私服使用的服务器。而要切换 web 服务器就需要将默认的 tomcat 服务器给排除掉,怎么排除呢?使用 exclusion 标签
    func (e *StoreProductController) Post(c *gin.Context) {
    var (
    dto  dto2.StoreProduct
    appG = app.Gin{C: c}
    )
    httpCode, errCode := app.BindAndValid(c, &dto)
    if errCode != constant.SUCCESS {
    appG.Response(httpCode, errCode, nil)
    return
    }
    productService := product_service.Product{
    Dto: dto,
    }
    model, err := productService.AddOrSaveProduct()
    if err != nil {
    appG.Response(http.StatusInternalServerError, constant.FAIL_ADD_DATA, nil)
    return
    }

    //发消息队列
    defer func() {
    operation := product.OperationCreate
    if dto.Id > 0 {
    operation = product.OperationUpdate
    }
    productMsg := models.ProductMsg{
    operation,
    &model,
    }
    msg, _ := json.Marshal(productMsg)
    p, o, e := mq.GetKafkaSyncProducer(mq.DefaultKafkaSyncProducer).Send(&sarama.ProducerMessage{
    Topic: product.Topic,
    Key:   mq.KafkaMsgValueStrEncoder(strconv.FormatInt(dto.Id, 10)),
    Value: mq.KafkaMsgValueEncoder(msg),
    },
    )
    if e != nil {
    global.LOG.Error("send product msg error ", e, "partition :", p, "offset :", o, "id :", dto.Id)
    }
    }()

    appG.Response(http.StatusOK, constant.SUCCESS, nil)

    }


  • 基于单片机的TFT-LCD液晶显示控制芯片选型表
    基于单片机的TFT-LCD液晶显示控制芯片选型表
  • 浅谈地下污水厂智能照明控制应用

    结合某地下污水厂项目,从结构、系统组成、系统功能、控制要求、场景模式等方面介绍了地下污水厂智能照明控制系统,探索了一套适用于地下污水厂的智能照明控制策略,以确保地下污水厂正常运行的照明需求。

  • 基于单片机的工业级液晶显示控制芯片
    TFT-LCD液晶显示控制芯片RA8889ML3N的优势:
    低功耗及功能强大:这款芯片最大支持分辨率为1366x2048,内置128Mb SDRAM,可为内容显示进行快速刷新,同时内置视频解码单元,支持JPEG/AVI硬解码播放,为普通单片机实现视频播放提供可能。
    支持多种接口:RA8889ML3N支持MCU端的8080/6800 8/16-bit 非同步并列接口和3/4线SPI及IIC串列接口,以及最大驱动1366x800分辨率的TFT LCD。
    显示功能强大:RA8889ML3N提供多段的显示记忆体缓冲区段,支持多图层功能,并提供画中画(PIP)、支持透明度控制与显示旋转镜像等显示功能。
    应用范围广:这款芯片广泛应用于自动化控制设备、电力监测控制、测量检测仪器仪表、电教设备、智能家电、医疗检测设备、车用仪表及工控自动化等领域。
  • 16、谷景电子贴片电感在智能电梯领域应用取得新进展
    16、谷景电子贴片电感在智能电梯领域应用取得新进展
  • RadarSensors_ARS408-21_cn数据手册
    RadarSensors_ARS408-21_cn数据手册
  • RadarSensors_ARS308-21_cn数据手册
    RadarSensors_ARS308-21_cn数据手册
  • 基于单片机的工业级液晶图形显示控制芯片RA8889ML3N-Datasheet
    TFT-LCD液晶显示控制芯片RA8889ML3N的优势:
    低功耗及功能强大:这款芯片最大支持分辨率为1366x2048,内置128Mb SDRAM,可为内容显示进行快速刷新,同时内置视频解码单元,支持JPEG/AVI硬解码播放,为普通单片机实现视频播放提供可能。
    支持多种接口:RA8889ML3N支持MCU端的8080/6800 8/16-bit 非同步并列接口和3/4线SPI及IIC串列接口,以及最大驱动1366x800分辨率的TFT LCD。
    显示功能强大:RA8889ML3N提供多段的显示记忆体缓冲区段,支持多图层功能,并提供画中画(PIP)、支持透明度控制与显示旋转镜像等显示功能。

    应用范围广:这款芯片广泛应用于自动化控制设备、电力监测控制、测量检测仪器仪表、电教设备、智能家电、医疗检测设备、车用仪表及工控自动化等领域。

    技术咨询与交流:QQ2851189731, 微信13760238805

  • 工业级液晶显示控制芯片RA8889ML3N原理图
    TFT-LCD液晶显示控制芯片RA8889ML3N的优势:
    低功耗及功能强大:这款芯片最大支持分辨率为1366x2048,内置128Mb SDRAM,可为内容显示进行快速刷新,同时内置视频解码单元,支持JPEG/AVI硬解码播放,为普通单片机实现视频播放提供可能。
    支持多种接口:RA8889ML3N支持MCU端的8080/6800 8/16-bit 非同步并列接口和3/4线SPI及IIC串列接口,以及最大驱动1366x800分辨率的TFT LCD。
    显示功能强大:RA8889ML3N提供多段的显示记忆体缓冲区段,支持多图层功能,并提供画中画(PIP)、支持透明度控制与显示旋转镜像等显示功能。
    应用范围广:这款芯片广泛应用于自动化控制设备、电力监测控制、测量检测仪器仪表、电教设备、智能家电、医疗检测设备、车用仪表及工控自动化等领域。
  • CS5511数据手册
    CS5511是一个将DP/eDP输入转换为LVDS信号的桥接芯片,此外,CS5511可以用作在DP/eDP输入到DP/eDP输出场景中桥接芯片
  • 风力发电机组机结构与原理-2018年-赵万清
    风力发电机组机结构与原理,中国电力出版社,PDF版本。
  • 特斯拉电路图.rar
    特斯拉电路图,欢迎大家下载
  • 近日,英国伦敦的可持续倡议(SMI)公布了2023年《地球宪章》徽章获奖名单。在这个名单中,全球共17家企业入选,中国企业仅占两席。最值得注目的是,海尔智家作为唯一获奖的中国家电企业,荣登榜单。 据了解,《地球宪章》徽章由现任英国国王查尔斯三世于2021年发起,旨在表彰对全球环境的可持续发展做出突出贡献的企业,鼓励企业将自然、人类和地球置于核心位置,通过创新和可持续的商业模式,共同应对全球气候挑战。获奖企业由来自环境、商业、政治和慈善界的全球专家小组经过严苛的评选选出。 海尔
    锦缎研究院 2023-12-06 12:41 53浏览
  • 摘要:根据CINNO Research产业统计数据,Q3'23国内智能手机屏下指纹识别占比创历史新高达45%,而侧边指纹识别占比较去年同期下降12%,后置指纹识别占比下降至1%,而前置指纹已淡出国内智能手机指纹识别市场。根据CINNO Research产业统计数据,受华为、荣耀热门机型销售的影响,Q3'23国内智能手机指纹识别搭载率上升至84%。图示:中国市场智能手机指纹识别技术别占比趋势来源: CINNO Research月度中国市场智能手机指纹识别应用趋势报告2023年第三季度,国内OLED
    CINNOResearch 2023-12-06 12:53 94浏览
  • 背景   随着汽车行业的不断迭代发展,市场及消费者对汽车提出了更高的要求,智能网联、自动驾驶等新技术的应用推动整车厂对车载芯片、汽车软件等方面投入了更多的精力,SOA(面向服务的架构)逐渐成为大多整车厂顺应市场趋势和技术趋势的首选。SOA架构使服务间的通讯变得更加简单,ECU更新、软件升级等变得更加灵活,使系统的健壮性和拓展性获得了大幅提高。但是在SOA架构开发阶段,由于市面上的IDL(接口描述语言)众多,例如FIDL、Protobuf、vCDL、ARXML、OMG IDL、CAN
    北汇信息 2023-12-06 11:41 76浏览
  • 光耦合器是一种在现代科技中发挥关键作用的设备,其应用领域横跨通信、医疗、工业等多个行业。光耦合器通过巧妙地将光电子器件结合起来,实现了光与电的高效转换和传输,为光电子领域的发展提供了强大的支持。光耦合器是什么光耦合器是一种用于隔离、耦合和传输光信号的器件。其主要功能是将一个光学系统的光信号转换成电信号,或者将电信号转换为光信号,以实现光与电之间的高效转换。在各类光电子设备中,光耦合器起到了桥梁的作用,实现了不同部分之间的无缝衔接。光耦合器的原理及结构光耦合器的基本原理是通过光电二极管和光电晶体管
    克里雅半导体科技 2023-12-06 10:58 31浏览
  • 来源:虹科汽车电子 虹科技术丨BabyLIN产品如何轻松搞定K线协议实现? 原文链接:https://mp.weixin.qq.com/s/LR7VHFQajjyw6s6bVDJmsg 欢迎关注虹科,为您提供最新资讯!   导读 为了实现K线通信,SDF-V3在协议部分中定义了新的协议类型KLine Raw。所有能够运行SDF-V3文件(LinWorks版本在V.2.29.4以上)并使用最新固件(固件版本在V.6.18以上)的BabyLIN设备都可以执行KLine Raw
    虹科电子科技 2023-12-06 14:42 119浏览
  • 导语:CINNO Research统计数据表明,Q3'23全球半导体设备厂商市场规模前十大公司合计超250亿美元,同比下降9%,环比增长3%。CINNO Research统计数据表明,Q3'23全球半导体设备厂商市场规模Top10营收合计超250亿美元,同比下降9%,环比增长3%。Q3'23全球半导体设备厂商市场规模排名Top10与1H'23的Top10设备商相比,日立高新(Hitachi High-Tech)排名跌出Top10,泰瑞达(Teradyne)排名回归第十。荷兰公司阿斯麦(ASML)
    CINNOResearch 2023-12-06 14:04 110浏览
  • 文章目录前言1 SD NAND概述2 代码说明3 记录Log前言本文基于 ESP32 芯片作为主控制器,测试 SD NAND 记录飞控 Log 功能。关于 MCU 的存储方面,以前基本上用内置的 E2PROM,或者是外置的 NOR Flash 就可以。随着物联网的兴起,MCU 的应用越来越广泛,逐渐的 MCU 会涉及到大容量的存储需求,用来存储音频,图片(GUI)、视频缓存、协议栈等等。传统的 E2PROM 和 NOR Flash 就不够用了。这个时候 MCU 可能就需要用到 NAND Flas
    雷龙发展 2023-12-06 18:18 17浏览
  • #这段代码是一个基于C语言的嵌入式程序,用于在HPMicro平台上运行。它的主要功能是初始化一个LED灯,并使其以一定的时间间隔闪烁。#以下是对代码的解析:#```c#include #include "board.h"#include "hpm_debug_console.h"#define LED_FLASH_PERIOD_IN_MS 300int main(void){    int u;    board_init(); // 初始化板子 
    丙丁先生 2023-12-06 14:22 108浏览
  • 近日,在传感器专家网的压力传感器专业交流群组中,有相关专家交流了目前我国压力传感器的一些情况。交流中指出,目前国内已有一些企业在做MEMS压力传感器芯片,在该领域国内相关企业总体来说技术差不多,精度等关键技术指标彼此间相差不大,但与国外压力传感器芯片巨头相比,精度等指标却有较大差距。传感器专家网https://www.sensorexpert.com.cn专注于传感器技术领域,致力于对全球前沿市场动态、技术趋势与产品选型进行专业垂直的服务,是国内领先的传感器产品查询与媒体信息服务平台。基于传感器
    传感器专家网 2023-12-06 11:03 52浏览
  • 国产光耦是近年来中国电子产业中备受关注的领域之一。在全球电子市场的竞争中,中国光电行业正逐渐崭露头角。本文将详细分析国产光耦的发展趋势,探讨其未来发展的关键因素与前景。国产光耦的现状国产光耦是一种用于电气隔离和信号传输的关键电子元器件,广泛应用于通信、工控、医疗、消费电子等领域。随着中国电子制造业的不断壮大,国产光耦在市场上逐渐崭露头角。目前,国产光耦已经实现了从技术到市场的多方面突破,主要体现在以下几个方面:1. 技术创新国产光耦制造商不断投入研发,推动光耦技术的创新与升级。新材料的应用、封装
    克里雅半导体科技 2023-12-06 10:56 36浏览
  • Delta-Sigma 模数转换器(Delta-Sigma)可为精密测量应用提供低功耗、低噪声前端。它可用于多种应用,具体取决于分辨率、采样速率和操作模式。它能够对 16 位音频进行高速低分辨率通信处理,并且能够对应变计、热电偶和其他高精度传感器进行高精度的 20 位低速转换。处理音频信息时, Delta-Sigma采用连续操作模式。当用于扫描多个传感器时, Delta-Sigma采用其中一个多样本采样模式。用于单点高分辨率测量时,Delta-Sigma采用单一样本采样模式。Delta-sigm
    blackguest 2023-12-07 00:34 24浏览
  • 2023年,对我来说,是充满挑战和收获的一年。这一年,我实现了很多的愿望,也收获了许多意外之喜。汗水与收获并存,艰难与惊喜共立。 工作上,我负责的项目在经过漫长的调研后终于立项了。在原理功能的实现过程中,我不仅学到了很多新的知识和技能,也经历了许多挫折和困难,一筹莫展时的长期等待和有效结果的欣喜。不放弃成功才会向你招手,最终努力得到了回报,项目成功立项,这让我感到非常自豪和满足。 除了工作上的成就,生活中我也有很多收获。这一年,我踩着夏天的尾巴购买了人生中属于自己的第一辆摩托车。 像风
    无言的朝圣 2023-12-06 10:48 108浏览
  •     今天看到一个麦肯锡的统计数据,2021年中国出口的电子产品占世界34%。越来越多的PCB组件在中国造出来,然后送往全世界。作为电子工程师,除了增加修养,不断实现良好的设计,也要减少电子垃圾,对国际上的主要环保要求有所了解。    ROHS  Restriction of Hazardous Substances  有毒物质限制        这个标准针对 6 类电子产品中常见的的有毒物质,
    电子知识打边炉 2023-12-06 22:21 119浏览
  • ​无论当下看不看机会,把握行情肯定是没错。 通过岗位数量,岗位要求(如对学历,技术点的要求)来了解行情是有效途径之一。 可以找我了解更多全国岗位。 【嵌入式软件工程师】 上海,风电行业国资企业,对学历有要求。 岗位职责: 1. 负责产品全周期研发,包括市场调研、客户需求技术转化、产品设计、产品制造、产品安装调试、产品测试验证和产品认证; 2. 负责产品失效根因分析,提供有效解决方案; 3. 负责组织供应商选择和产品质量管控; 4. 组织编制产品开发技
    落子无悔 2023-12-06 13:27 63浏览
  • 12月5日中国台湾地区“国科委”发布了一份包含22项核心关键技术的清单,这些技术具有“主导优势与保护急迫性”,列入清单的技术在出口方面将面临审查。传感器专家网https://www.sensorexpert.com.cn专注于传感器技术领域,致力于对全球前沿市场动态、技术趋势与产品选型进行专业垂直的服务,是国内领先的传感器产品查询与媒体信息服务平台。基于传感器产品与技术,对广大电子制造从业者与传感器制造者提供精准的匹配与对接。技术清单中,包含多项传感器技术,以及14纳米以下制程芯片制造技术等半导
    传感器专家网 2023-12-06 19:57 24浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦