STM32MP157A-DK1测评 (4) UART与SPI设备访问
<p> 板子上 ST-Link 的虚拟串口是连在 STM32MP157A 的 UART4 上的。如果在系统插卡状态,启动到 u-boot 阶段的时候在终端里键入字符使之进入交互模式,再使用 OpenOCD 对MPU进行调试,可以获得一个更好的软件环境。我发现这个状态下ROM、SYSRAM都没有被限制访问,外部的SDRAM也初始化过了可以用。</p><p></p>
<p> 想必 UART4 是已经初始化过了,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 />
while(!(UART4->ISR & USART_ISR_TXE));<br />
UART4->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 />
while(*s)<br />
uart_wchar(*s++);<br />
}</strong></span></span><br />
看起来就跟 STM32 MCU 一样的。这就对了。</p>
<p> </p>
<p> 再试试 SPI. 从扩展的插针那里可以找到 SPI4 的相关引脚,可以连个 SPI 接口的 LCD 模块试一下。</p>
<p> SPI5 的设备地址是 0x44009000. 但我试了一下向寄存器写值——无效。</p>
<p> 按照 STM32 MCU 的经验,这是 SPI5 的设备时钟没有使能,需要对 RCC 中的 APBxENR 进行操作。那么就搜索一下手册里 SPI5EN 关键字,立即找到一个:</p>
<p>有这个寄存器位,不过不在 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> RCC->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 />
SPI5->CR1=0; // disable (if already configured)<br />
SPI5->IFCR = 0xFFFF; // clear all flags<br />
SPI5->CFG1=SPI_CFG1_MBR_2|SPI_CFG1_MBR_1 // master clock/128<br />
|0<<SPI_CFG1_FTHLV_Pos // FIFO level: 1-data<br />
|7<<SPI_CFG1_DSIZE_Pos; // data size: 8-bit<br />
SPI5->CFG2=SPI_CFG2_SSOE|SPI_CFG2_MASTER;<br />
SPI5->CR2=0; // TSIZE and reload<br />
SPI5->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 />
int i;<br />
if(!sz)<br />
return;<br />
if(SPI5->CR1 & SPI_CR1_CSTART)<br />
return;<br />
SPI5->CR2=sz;<br />
SPI5->CR1 |= SPI_CR1_CSTART;<br />
for(i=0;i<sz;i++)<br />
{<br />
while(!(SPI5->SR & SPI_SR_TXP));<br />
*(volatile uint8_t *)&SPI5->TXDR = buf;<br />
}<br />
while(!(SPI5->SR & SPI_SR_EOT))<br />
{}<br />
while((GPIOF->IDR & 1<<6)==0) // NSS Low<br />
{}<br />
SPI5->IFCR = SPI_IFCR_EOTC;<br />
}</span></strong></span><br />
STM32MP157A 的 SPI master 可以自己管理 NSS 脚,不像老的 SPI 那样不传输的时候 master NSS 输出一直是低。</p>
<p> </p>
<p> 最后上图,测试LCD显示的效果(从UART接收字符,按点阵字库的数据在 96x64 LCD 上画出来符号。程序也就沿用我为 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) $<
%.bin : %.elf
arm objcopy-Obinary $< $@</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);
} >CODE
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >CODE
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >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 */
} >RAM AT >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 */
} >RAM
_estack = 0x2FFC8000;
}</code></pre>
<p> 程序用 OpenOCD load_image 命令装到 0x2FFC8000 处运行。</p>
厉害啊
页:
[1]