cruelfox 发表于 2020-4-19 22:13

STM32MP157A-DK1测评 (4) UART与SPI设备访问

<p>  板子上 ST-Link 的虚拟串口是连在 STM32MP157A 的 UART4 上的。如果在系统插卡状态,启动到 u-boot 阶段的时候在终端里键入字符使之进入交互模式,再使用 OpenOCD 对MPU进行调试,可以获得一个更好的软件环境。我发现这个状态下ROM、SYSRAM都没有被限制访问,外部的SDRAM也初始化过了可以用。</p>

<p></p>

<p>  想必&nbsp;UART4&nbsp;是已经初始化过了,u-boot需要用它。那么我直接访问 UART4 的寄存器来发送字符如何?从手册里查到 UART4 的基本地址是 0x40010000, 发送用数据寄存器 TDR 偏移量是 0x28. 那么先试试用 OpenOCD 的 mwb 命令往 0x40010028 地址写一个字节数据,看串口终端是否有内容。一试果然有字符收到。</p>

<p>  在这个环境下,用 UART4 来发送数据就和 MCU 的操作差不多了。现在不碰中断、DMA这些,就用查询状态寄存器的方式,写个发送字符的函数:</p>

<p><strong><span style="color:#2980b9;"><span style="font-family:STHeiti;">void uart_wchar(uint8_t ch)<br />
{<br />
&nbsp;&nbsp; &nbsp;while(!(UART4-&gt;ISR &amp; USART_ISR_TXE));<br />
&nbsp;&nbsp; &nbsp;UART4-&gt;TDR = ch;<br />
}</span></span></strong></p>

<p>以及输出一个字符串的函数:</p>

<p><span style="color:#2980b9;"><span style="font-family:STHeiti;"><strong>void uart_wstr(const char *s)<br />
{<br />
&nbsp;&nbsp; &nbsp;while(*s)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;uart_wchar(*s++);<br />
}</strong></span></span><br />
  看起来就跟 STM32 MCU 一样的。这就对了。</p>

<p>&nbsp;</p>

<p>  再试试 SPI. &nbsp;从扩展的插针那里可以找到 SPI4 的相关引脚,可以连个 SPI 接口的 LCD 模块试一下。</p>

<p>  SPI5 的设备地址是 0x44009000. 但我试了一下向寄存器写值&mdash;&mdash;无效。</p>

<p>  按照 STM32 MCU 的经验,这是 SPI5 的设备时钟没有使能,需要对 RCC 中的 APBxENR 进行操作。那么就搜索一下手册里 SPI5EN 关键字,立即找到一个:</p>

<p>有这个寄存器位,不过不在&nbsp;APB2ENR 寄存器,而是 MP_APB2ENSETR (还有一个 MP_APB2ENCLRR用来清除),就把 STM32 MCU 那样用一个寄存器置写0/1换成了用两个寄存器分别置位/复位的操作方式。特别的,这个寄存器是给 MPU (Cortex-A7)用的,给MCU (Cortex-M4)的还有 MC_APB2ENSETR 寄存器。看来,STM32MP157 上面对硬件资源的分配是更复杂的,也许 A7 核启用了对 SPI5 的访问以后,M4 核就不能访问了,待后面我再测试是否是这样。</p>

<p>  于是我在程序中用了一行</p>

<p><span style="color:#2980b9;"><span style="font-family:STHeiti;"><strong>&nbsp;&nbsp; &nbsp;RCC-&gt;MP_APB2ENSETR = RCC_MC_APB2ENSETR_SPI5EN;</strong></span></span><br />
来启用 SPI5 设备,再对它的寄存器进行初始化。</p>

<p>  看 SPI5 和以往 MCU 的 SPI 是否兼容,可以通过看寄存器表来判断。</p>

<p>  这寄存器设置和我用过的 F0/F4/L4 的SPI寄存器差别明显,看来是升级了。不过,从手册里居然看到了 H7 的字样(难道是从 STM32H7 开始升级的)。</p>

<p>和我用过的老的 SPI 比起码 FIFO 是升级了。那就学着用用,写段程序。</p>

<p><span style="color:#2980b9;"><strong><span style="font-family:STHeiti;">void config_spi5(void)<br />
{<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CR1=0;&nbsp;&nbsp; &nbsp;// disable (if already configured)<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;IFCR = 0xFFFF; // clear all flags<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CFG1=SPI_CFG1_MBR_2|SPI_CFG1_MBR_1 // master clock/128<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;|0&lt;&lt;SPI_CFG1_FTHLV_Pos&nbsp;&nbsp; &nbsp;// FIFO level: 1-data<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;|7&lt;&lt;SPI_CFG1_DSIZE_Pos;&nbsp;&nbsp; &nbsp;// data size: 8-bit<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CFG2=SPI_CFG2_SSOE|SPI_CFG2_MASTER;<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CR2=0;&nbsp;&nbsp; &nbsp;// TSIZE and reload<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CR1=SPI_CR1_SPE;<br />
}</span></strong></span></p>

<p>  依然可以用 MCU 的思维来写代码,就是些寄存器嘛,可能跟 STM32H7 的写法一致。最后我实现的发送指定长度数据的函数是这样的(因为给LCD用,不考虑接收了)</p>

<p><span style="color:#2980b9;"><strong><span style="font-family:STHeiti;">void spi_write(const uint8_t *buf, uint16_t sz)<br />
{<br />
&nbsp;&nbsp; &nbsp;int i;<br />
&nbsp;&nbsp; &nbsp;if(!sz)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return;<br />
&nbsp;&nbsp; &nbsp;if(SPI5-&gt;CR1 &amp; SPI_CR1_CSTART)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return;<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CR2=sz;<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;CR1 |= SPI_CR1_CSTART;<br />
&nbsp;&nbsp; &nbsp;for(i=0;i&lt;sz;i++)<br />
&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;while(!(SPI5-&gt;SR &amp; SPI_SR_TXP));<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;*(volatile uint8_t *)&amp;SPI5-&gt;TXDR = buf;<br />
&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;while(!(SPI5-&gt;SR &amp; SPI_SR_EOT))<br />
&nbsp;&nbsp; &nbsp;{}<br />
&nbsp;&nbsp; &nbsp;while((GPIOF-&gt;IDR &amp; 1&lt;&lt;6)==0)&nbsp;&nbsp; &nbsp;// NSS Low<br />
&nbsp;&nbsp; &nbsp;{}<br />
&nbsp;&nbsp; &nbsp;SPI5-&gt;IFCR = SPI_IFCR_EOTC;<br />
}</span></strong></span><br />
  STM32MP157A 的 SPI master 可以自己管理 NSS 脚,不像老的 SPI 那样不传输的时候 master NSS 输出一直是低。</p>

<p>&nbsp;</p>

<p>  最后上图,测试LCD显示的效果(从UART接收字符,按点阵字库的数据在 96x64 LCD 上画出来符号。程序也就沿用我为&nbsp;MCU 写的程序,移植到这里)。</p>

<p></p>

<p>  因为没有用中断、DMA操作,到目前程序写法和 Cortex-m MCU 完全一致。为了方便编译,写一个 Makefile</p>

<pre>
<code>default: run.bin

CC =arm gcc -c
LD =arm gcc -nostartfiles
CFLAGS =-g -Os -std=gnu99 -mcpu=cortex-a7 -mthumb
LDFLAGS=-T sysram.ld -mcpu=cortex-a7 -mthumb
INC =-I.

OBJS = test2.o

run.elf : $(OBJS)
        $(LD) $(LDFLAGS) $^ -o $@

%.o : %.c
        $(CC) $(CFLAGS) $(INC) $&lt;

%.bin : %.elf
        arm objcopy-Obinary $&lt; $@</code></pre>

<p>以及 Linker Script, 用来确定地址</p>

<pre>
<code>
MEMORY
{
CODE (rx)      : ORIGIN = 0x2FFC8000, LENGTH = 32K
RAM (xrw)      : ORIGIN = 0x2FFC1000, LENGTH = 28K
}

SECTIONS
{
.entry :
{
    . = ALIGN(4);
    KEEP(*(.text.entry))
    . = ALIGN(4);
} &gt;CODE

.text :
{
    . = ALIGN(4);
    *(.text)         /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */

    . = ALIGN(4);
    _etext = .;      /* define a global symbols at end of code */
} &gt;CODE

.rodata :
{
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)      /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
} &gt;CODE

_sidata = LOADADDR(.data);

.data :
{
    . = ALIGN(4);
    _sdata = .;      /* create a global symbol at data start */
    *(.data)         /* .data sections */
    *(.data*)          /* .data* sections */
    . = ALIGN(4);
    _edata = .;      /* define a global symbol at data end */
} &gt;RAM AT &gt;CODE

. = ALIGN(4);
.bss :
{
    _sbss = .;         /* define a global symbol at bss start */
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
} &gt;RAM

_estack = 0x2FFC8000;

}</code></pre>

<p>  程序用 OpenOCD load_image 命令装到 0x2FFC8000 处运行。</p>

hotsauce1861 发表于 2020-4-24 08:53

厉害啊
页: [1]
查看完整版本: STM32MP157A-DK1测评 (4) UART与SPI设备访问