cruelfox 发表于 2020-4-26 21:55

STM32MP157A-DK1测评 (6) Cortex-A7 Linux下的程序环境

<div class='showpostmsg'><p>  官方提供的&nbsp;STM32MP1_Yocto_SDK_V1.2.0.xz 开发包提供了&nbsp;STM32MP157 Linux应用程序开发需要的工具。解开以后里面的安装文件是一个巨大的 .sh 脚本文件(数据包含在内),需要在 x86_64 Linux 环境下运行。安装之后主要会得到两个目录树(均位于sysroots子目录下):</p>

<p>  <span style="color:#8e44ad;"><strong>cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi</strong></span></p>

<p>  <span style="color:#8e44ad;"><strong>x86_64-ostl_sdk-linux</strong></span></p>

<p>  第一个是似乎完整的&nbsp;ARM Linux 系统目录,里面的程序是给 STM32MP157 运行的。可以把这些目录复制到 SD 卡的一个分区上去实验,这上面有很多开发用的文件,还有调试用的文件。第二个是运行于 x86_64 Linux 下的开发工具,包含了开发 STM32MP157 Linux 程序需要的交叉编译工具,还有QEMU模拟器、OpenOCD、x86_64编译工具、Python等。<br />
  值得说一下的是,x86_64-ostl_sdk-linux 里面的程序对运行它的 Linux 环境依赖度很小。我当初自己编译 32位 OpenOCD 来用是因为要装(编译)一套 x86_64 环境才能运行这里面的 OpenOCD;而后来等我编译了一个 x86_64 内核之后,发现ST的包是自带所有运行库的,安装过程中(用了一个Python程序)把 dynamic linker 的路径写死到每个执行文件里面了,就不依赖原有系统上的库了,只要&nbsp;x86_64 内核版本够就行。</p>

<p>&nbsp;</p>

<p>  要编译一个最简单的能在开发板 Linux 下运行的 C&nbsp;程序需要用到以上这两个目录的内容。虽然第一个里面的程序都不是给 PC 运行的,但编译程序时需要的头文件和库文件在这里;第二个有 PC 上运行的编译工具但缺少了目标系统的C运行库。</p>

<p>  先做一个测试程序出来:</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="hljs language-cpp"><span class="hljs-keyword">int</span> main()
{
   <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;From Linux (Cortex-A7) 0x%X\n&quot;</span>,main);
}</code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background-color: rgba(220, 220, 220, 0.498039); background-image: url(https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png); top: -15px; left: 0px; display: block; background-position: initial initial; background-repeat: initial initial;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>  我把 #include&lt;stdio.h&gt; 给省去了,为的是编译C源文件时不使用头文件,GCC会警告但这儿没关系。在 PC Linux 下执行编译命令:</p>

<p>  <span style="font-size:16px;"><span style="font-family:Courier;"><span style="background-color:#ffff99;">$</span></span></span><strong><span style="font-size:16px;"><span style="font-family:Courier;"><span style="background-color:#ffff99;"> arm-ostl-linux-gcc -c -O -mcpu=cortex-a7 test.c</span></span></span></strong></p>

<p>然后就生成了一个 test.o 目标文件。不妨查看一下反汇编代码:</p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">$ <strong>arm-ostl-linux-objdump -d test.o</strong></span></span></p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">test.o: &nbsp; &nbsp; file format elf32-littlearm</span></span></p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">Disassembly of section .text:</span></span></p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">00000000 &lt;main&gt;:</span><br />
<span style="background-color:#ecf0f1;">&nbsp; &nbsp;0: &nbsp; e92d4010 &nbsp; &nbsp; &nbsp; &nbsp;push &nbsp; &nbsp;{r4, lr}</span><br />
<span style="background-color:#ecf0f1;">&nbsp; &nbsp;4: &nbsp; e3001000 &nbsp; &nbsp; &nbsp; &nbsp;movw &nbsp; &nbsp;r1, #0</span><br />
<span style="background-color:#ecf0f1;">&nbsp; &nbsp;8: &nbsp; e3401000 &nbsp; &nbsp; &nbsp; &nbsp;movt &nbsp; &nbsp;r1, #0</span><br />
<span style="background-color:#ecf0f1;">&nbsp; &nbsp;c: &nbsp; e3000000 &nbsp; &nbsp; &nbsp; &nbsp;movw &nbsp; &nbsp;r0, #0</span><br />
<span style="background-color:#ecf0f1;">&nbsp; 10: &nbsp; e3400000 &nbsp; &nbsp; &nbsp; &nbsp;movt &nbsp; &nbsp;r0, #0</span><br />
<span style="background-color:#ecf0f1;">&nbsp; 14: &nbsp; ebfffffe &nbsp; &nbsp; &nbsp; &nbsp;bl &nbsp; &nbsp; &nbsp;0 &lt;printf&gt;</span><br />
<span style="background-color:#ecf0f1;">&nbsp; 18: &nbsp; e3a00000 &nbsp; &nbsp; &nbsp; &nbsp;mov &nbsp; &nbsp; r0, #0</span><br />
<span style="background-color:#ecf0f1;">&nbsp; 1c: &nbsp; e8bd8010 &nbsp; &nbsp; &nbsp; &nbsp;pop &nbsp; &nbsp; {r4, pc}</span></span><br />
  这是使用的 ARM 模式的指令,调用了一个入口地址未定的子程序 (printf)</p>

<p>&nbsp;</p>

<p>  然后要生成开发板的 Linux 下运行的可执行文件,需要 (1) 和运行库链接,得以使用&nbsp;printf 函数的实现。(2) 链接上启动代码,由启动代码初始化环境,调用 main 函数。现在没有直接用 GCC 命令生成可执行文件,是因为需要的C库文件不在默认路径下(交叉工具目录不包含)。一试的话就会发现报错:crt1.o &nbsp;crti.o crtbegin.o crtend.o crtn.o 目标文件找不到,-lc, -lgcc 等要链接的库文件找不到。</p>

<p>  试图手工解决一下,将文件路径用参数提供给 GCC,连接刚才编译过的目标文件:</p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">$ <strong>arm-ostl-linux-gcc test.o -B /opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/lib -L /opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/lib -L /opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/lib</strong></span><br />
<span style="background-color:#ecf0f1;">/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: <span style="color:#c0392b;">skipping incompatible</span> /lib/libc.so.6 when searching for /lib/libc.so.6</span><br />
<span style="background-color:#ecf0f1;">/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: <span style="color:#c0392b;">cannot find</span> /lib/libc.so.6</span><br />
<span style="background-color:#ecf0f1;">/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: <span style="color:#c0392b;">cannot find</span> /usr/lib/libc_nonshared.a</span><br />
<span style="background-color:#ecf0f1;">/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: <span style="color:#c0392b;">cannot find</span> /lib/ld-linux-armhf.so.3</span><br />
<span style="background-color:#ecf0f1;">collect2: error: ld returned 1 exit status</span></span><br />
  尽管依赖的文件路径已经找到了,但在 libc.so 的处理上出了问题。因为 LD 链接 libc.so 的时候(这是一个文本文件)被告诉使用 /lib/libc.so.6 和 /usr/lib/libc_nonshared.a 文件,这是我本机x86 Linux系统目录里的文件,当然不正确。解决办法之一是编辑那个 libc.so 文件(或者编辑后另存一个在别的地方),把SDK的路径写进去。而 ld-linux-armhf.so.3 这个文件,因为和本机系统没有冲突,就复制一个到 /lib 下好了。</p>

<p>  经过这番调整以后,库文件路径问题解决了,但又出现新的错误:</p>

<p>/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: error: a.out uses VFP register arguments, test.o does not<br />
/opt/stm32mp1/sysroots/x86_64-ostl_sdk-linux/usr/libexec/arm-ostl-linux-gnueabi/gcc/arm-ostl-linux-gnueabi/8.2.0/real-ld: failed to merge target specific data of file test.o<br />
collect2: error: ld returned 1 exit status</p>

<p>  这是链接程序报说 test.o 与要生成的可执行文件在参数传递上不匹配,即 ABI 不兼容。在 MCU 开发中也可能发生这样的问题。我在编译 test.c 的时候没有指定用 VFP(浮点处理器),于是重新编译一下,加上 -mfloat-abi=hard 选项。</p>

<p>  然后就编译成功了。把可执行文件用网络传到开发板上试运行:</p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./test</strong></span><br />
<span style="background-color:#ecf0f1;">From Linux (Cortex-A7) 0x103D8</span></span></p>

<p>  程序运行成功。main() 函数的入口地址是 0x103D8</p>

<p>  还有一种生成可执行文件的办法是在开发板上运行链接程序 arm-ostl-linux-gnueabi-ld (包含在SDK的&nbsp;cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi 目录里面),利用 PC 上交叉编译得到(因为 SDK 没有提供 cortex-a 上运行的 GCC, 只提供了 binutils 软件)的目标文件 test.o. 事先需要把用得着的文件传到开发板Linux文件系统中。</p>

<p><span style="font-family:Courier;"><span style="background-color:#ffffcc;">root@stm32mp1:/tmp# <strong>arm-ostl-linux-gnueabi-ld test.o crt1.o crti.o crtn.o /lib/libc.so.6 --as-needed /usr/lib/libc_nonshared.a -I /lib/ld-linux-armhf.so.3 -o test</strong></span></span></p>

<p>&nbsp;</p>

<p>  注意,回顾 STM32MP157A 的内存地址排布,SDRAM 的地址空间在 0xC0000000 以上,而 0x103D8 这本来是 ROM 空间的地址。很明显,这里 0x103D8 不是实际的物理地址,而是<strong>虚拟内存地址</strong>&mdash;&mdash;Cortex-A7具有MMU (内存管理单元),实现了地址转换。</p>

<p>  为了方便测试&nbsp;Linux 程序看到的内存空间是什么样的,把程序改写一下:</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="hljs language-cpp"><span class="hljs-preprocessor">#include&lt;stdio.h&gt;</span>
<span class="hljs-preprocessor">#include&lt;stdlib.h&gt;</span>
<span class="hljs-keyword">int</span> main(<span class="hljs-keyword">int</span> argc, <span class="hljs-keyword">char</span> *argv[])
{
<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> addr, *ptr;
<span class="hljs-keyword">char</span> *ep;
<span class="hljs-keyword">int</span> i;
<span class="hljs-keyword">if</span>(argc&lt;<span class="hljs-number">2</span>)
{
   <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Address?\n&quot;</span>);
   <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
}
addr=strtoul(argv[<span class="hljs-number">1</span>],&amp;ep,<span class="hljs-number">16</span>);
<span class="hljs-keyword">if</span>(ep==argv[<span class="hljs-number">1</span>])
{
   <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;Bad number.\n&quot;</span>);
   <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;
}
ptr=(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> *)(addr &amp; <span class="hljs-number">0xFFFFFFFC</span>);
<span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;MEM 0x%08X read:&quot;</span>, ptr);
<span class="hljs-keyword">for</span>(i=<span class="hljs-number">0</span>;i&lt;<span class="hljs-number">4</span>;i++)
{
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot; %08X&quot;</span>, *ptr);
    ptr++;
}
<span class="hljs-built_in">printf</span>(<span class="hljs-string">&quot;\n&quot;</span>);
}</code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background-color: rgba(220, 220, 220, 0.498039); background-image: url(https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png); top: -15px; left: 0px; display: block; background-position: initial initial; background-repeat: initial initial;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>  编译之:</p>

<p><span style="font-size:16px;"><span style="font-family:Courier;"><span style="background-color:#ffffcc;">$ <strong>arm-ostl-linux-gcc rmem.c -I/opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/include -B&nbsp;/opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/lib/ -L.&nbsp;-L/opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/usr/lib -L/opt/stm32mp1/sysroots/cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi/lib -mfloat-abi=hard -mcpu=cortex-a7 -O -o rmem</strong></span></span></span></p>

<p>测试:</p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 103d8</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x000103D8 read: 00010511 E59F3014 E59F2014 E08F3003</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 10000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x00010000 read: 464C457F 00010101 00000000 00000000</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 0</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 8000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 2ffc8000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem c0001000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 40000000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem 50000000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rmem e0000000</strong></span><br />
<span style="background-color:#ecf0f1;">Segmentation fault (core dumped)</span></span><br />
  结果是&mdash;&mdash;除了程序自己用到的一小段地址空间可读外,整个32-bit地址空间几乎是禁止直接访问的。系统将自己保护起来了,这和在评测的(3) (4)篇中,没有使用操作系统环境运行程序的效果不一样。保护起来可以防止恶意的程序、有bug的程序破坏系统,对硬件访问加也了一层控制(比如现在不能直接访问GPIO的寄存器喽)。</p>

<p>&nbsp;</p>

<p>  在 Linux 下运行的程序,要访问硬件设备,正规方式是通过内核中的硬件驱动程序作桥梁。对习惯 MCU 开发方式的人来说可能这样有些绕,但从安全性、可移植性角度优势就大多了。和硬件设备寄存器、IRQ、DMA等打交道的事情,就交给内核程序开发者。</p>

<p>  比如操作 GPIO,可以从系统设备 <span style="color:#d35400;"><strong>/dev/gpio</strong></span>xxxx 下手;操作串口可以从 <span style="color:#d35400;"><strong>/dev/tty</strong></span>xxxx 下手。有的硬件可以用脚本程序操作,不需要编写C代码。</p>

<p>&nbsp;</p>

<p>  那么有没有非正规的办法,直接在程序中访问硬件设备的寄存器呢?我网上搜了一下,一种可能的办法是用 <span style="color:#d35400;"><strong>/dev/mem</strong></span> 设备访问物理地址空间。仿照个写个读物理地址内容的程序:</p>

<pre>
<code class="language-cpp">#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;
#include&lt;unistd.h&gt;
#include&lt;sys/mman.h&gt;
#include&lt;fcntl.h&gt;
int main(int argc, char *argv[])
{
    unsigned int addr, base, *ptr, *vb;
    char *ep;
    int i, fd, offset;
    if(argc&lt;2)
    {
       printf("Address?\n");
       return 1;
    }
    addr=strtoul(argv,&amp;ep,16);
    if(ep==argv)
    {
       printf("Bad number.\n");
       return 2;
    }
    base=addr &amp;0xFFFFFC00;
    offset=(addr-base)&amp;0xFF0;
    fd=open("/dev/mem",O_RDWR|O_SYNC);
    if(!fd)
    {
       printf("Failed to access /dev/mem\n");
       return 3;
    }
    vb=mmap(0,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,base);
    ptr=(unsigned int)vb+offset;
    printf("MEM 0x%08X (alias 0x%08X) read:", base+offset,ptr);
    fflush(stdout);
    for(i=0;i&lt;4;i++)
    {
      printf(" %08X", *ptr);
      ptr++;
    }
    close(fd);
    munmap(vb,1024);
    printf("\n");
}</code></pre>

<p>  编译运行测试:</p>

<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 0x2ffc1000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x2FFC1000 (alias 0xB6F8E000) read: 668148DF 0450BAC1 477FF993 4E5E4A05</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 0x10000000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x10000000 (alias 0xB6FE8000) read: 6C591EEC 791E5FFE 504168EF 28AA1917</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 0x0</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x00000000 (a[ &nbsp;147.492075] Unhandled fault: external abort on non-linefetch (0x008) at 0xb6efc000</span><br />
<span style="background-color:#ecf0f1;">[ &nbsp;147.499632] pgd = c216a94f</span><br />
<span style="background-color:#ecf0f1;">[ &nbsp;147.502326] *pgd=cf77b835, *pte=00000783, *ppte=00000e33</span><br />
<span style="background-color:#ecf0f1;">lias 0xB6EFC000) read:Bus error (core dumped)</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 44004000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x44004000 (alias 0xB6F69000) read: 00000000 00000000 00000000 00000000</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 40010000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x40010000 (alias 0xB6F1A000) read: 2400000D 00800000 54800000 0000022C</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 50002000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x50002000 (alias 0xB6F81000) read: 00000000 00000000 00000000 00000000</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 5800D000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x5800D000 (alias 0xB6F1F000) read: 01000010 00001212 0002A026 00000000</span><br />
<span style="background-color:#ecf0f1;">root@stm32mp1:/tmp# <strong>./rphy 5a003000</strong></span><br />
<span style="background-color:#ecf0f1;">MEM 0x5A003000 (a[ &nbsp;298.718977] Unhandled fault: external abort on non-linefetch (0x1008) at 0xb6ff8008</span><br />
<span style="background-color:#ecf0f1;">[ &nbsp;298.726616] pgd = c77eb1e7</span><br />
<span style="background-color:#ecf0f1;">[ &nbsp;298.729312] *pgd=cf722835, *pte=5a003783, *ppte=5a003e33</span><br />
<span style="background-color:#ecf0f1;">lias 0xB6FF8000) read:Bus error (core dumped)</span></span><br />
  也不是所有物理地址都可以访问。有的访问会出错,结果就是程序崩溃。GPIOA的寄存器 (在0x50002000处) 读不到真实值。UART4 的寄存器好象能被访问。</p>

<p>  那么用这个办法,向 UART4 的 TDR 寄存器写数据会怎样?(UART4-&gt;TDR 地址是 0x40010028) 现在需要对重定位的虚拟地址进行写操作&mdash;&mdash;mmap() 返回的地址加上偏移量。实验结果是在串口终端里面看到了字符!</p>

<p>  这意味着 Linux 下运行的程序,只要有办法(绕过MMU的保护)访问片上设备寄存器的物理地址,就仍然可以用直接寄存器访问去操控硬件&mdash;&mdash;虽然一般没有必要这么做。</p>

<p><br />
<b><font color="#5E7384">此内容由EEWORLD论坛网友<font size="3">cruelfox</font>原创,如需转载或用于商业用途需征得作者同意并注明出处</font></b></p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

okhxyyo 发表于 2020-4-27 14:37

<p>精彩!这个测评活动即将结束了哦~将会在4月29日结束。如果还有内容未完成,记得赶紧写哦</p>

hotsauce1861 发表于 2020-4-28 13:06

<p>认真看完,  </p>

<p><strong>这意味着 Linux 下运行的程序,只要有办法(绕过MMU的保护)访问片上设备寄存器的物理地址,就仍然可以用直接寄存器访问去操控硬件&mdash;&mdash;虽然一般没有必要这么做。</strong></p>

<p>&nbsp;</p>

<p>这个想法很厉害<img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/onion/Onion--101.gif" width="50" /></p>
页: [1]
查看完整版本: STM32MP157A-DK1测评 (6) Cortex-A7 Linux下的程序环境