板子上 ST-Link 的虚拟串口是连在 STM32MP157A 的 UART4 上的。如果在系统插卡状态,启动到 u-boot 阶段的时候在终端里键入字符使之进入交互模式,再使用 OpenOCD 对MPU进行调试,可以获得一个更好的软件环境。我发现这个状态下ROM、SYSRAM都没有被限制访问,外部的SDRAM也初始化过了可以用。
想必 UART4 是已经初始化过了,u-boot需要用它。那么我直接访问 UART4 的寄存器来发送字符如何?从手册里查到 UART4 的基本地址是 0x40010000, 发送用数据寄存器 TDR 偏移量是 0x28. 那么先试试用 OpenOCD 的 mwb 命令往 0x40010028 地址写一个字节数据,看串口终端是否有内容。一试果然有字符收到。
在这个环境下,用 UART4 来发送数据就和 MCU 的操作差不多了。现在不碰中断、DMA这些,就用查询状态寄存器的方式,写个发送字符的函数:
void uart_wchar(uint8_t ch)
{
while(!(UART4->ISR & USART_ISR_TXE));
UART4->TDR = ch;
}
以及输出一个字符串的函数:
void uart_wstr(const char *s)
{
while(*s)
uart_wchar(*s++);
}
看起来就跟 STM32 MCU 一样的。这就对了。
再试试 SPI. 从扩展的插针那里可以找到 SPI4 的相关引脚,可以连个 SPI 接口的 LCD 模块试一下。
SPI5 的设备地址是 0x44009000. 但我试了一下向寄存器写值——无效。
按照 STM32 MCU 的经验,这是 SPI5 的设备时钟没有使能,需要对 RCC 中的 APBxENR 进行操作。那么就搜索一下手册里 SPI5EN 关键字,立即找到一个:
有这个寄存器位,不过不在 APB2ENR 寄存器,而是 MP_APB2ENSETR (还有一个 MP_APB2ENCLRR用来清除),就把 STM32 MCU 那样用一个寄存器置写0/1换成了用两个寄存器分别置位/复位的操作方式。特别的,这个寄存器是给 MPU (Cortex-A7)用的,给MCU (Cortex-M4)的还有 MC_APB2ENSETR 寄存器。看来,STM32MP157 上面对硬件资源的分配是更复杂的,也许 A7 核启用了对 SPI5 的访问以后,M4 核就不能访问了,待后面我再测试是否是这样。
于是我在程序中用了一行
RCC->MP_APB2ENSETR = RCC_MC_APB2ENSETR_SPI5EN;
来启用 SPI5 设备,再对它的寄存器进行初始化。
看 SPI5 和以往 MCU 的 SPI 是否兼容,可以通过看寄存器表来判断。
这寄存器设置和我用过的 F0/F4/L4 的SPI寄存器差别明显,看来是升级了。不过,从手册里居然看到了 H7 的字样(难道是从 STM32H7 开始升级的)。
和我用过的老的 SPI 比起码 FIFO 是升级了。那就学着用用,写段程序。
void config_spi5(void)
{
SPI5->CR1=0; // disable (if already configured)
SPI5->IFCR = 0xFFFF; // clear all flags
SPI5->CFG1=SPI_CFG1_MBR_2|SPI_CFG1_MBR_1 // master clock/128
|0<<SPI_CFG1_FTHLV_Pos // FIFO level: 1-data
|7<<SPI_CFG1_DSIZE_Pos; // data size: 8-bit
SPI5->CFG2=SPI_CFG2_SSOE|SPI_CFG2_MASTER;
SPI5->CR2=0; // TSIZE and reload
SPI5->CR1=SPI_CR1_SPE;
}
依然可以用 MCU 的思维来写代码,就是些寄存器嘛,可能跟 STM32H7 的写法一致。最后我实现的发送指定长度数据的函数是这样的(因为给LCD用,不考虑接收了)
void spi_write(const uint8_t *buf, uint16_t sz)
{
int i;
if(!sz)
return;
if(SPI5->CR1 & SPI_CR1_CSTART)
return;
SPI5->CR2=sz;
SPI5->CR1 |= SPI_CR1_CSTART;
for(i=0;i<sz;i++)
{
while(!(SPI5->SR & SPI_SR_TXP));
*(volatile uint8_t *)&SPI5->TXDR = buf;
}
while(!(SPI5->SR & SPI_SR_EOT))
{}
while((GPIOF->IDR & 1<<6)==0) // NSS Low
{}
SPI5->IFCR = SPI_IFCR_EOTC;
}
STM32MP157A 的 SPI master 可以自己管理 NSS 脚,不像老的 SPI 那样不传输的时候 master NSS 输出一直是低。
最后上图,测试LCD显示的效果(从UART接收字符,按点阵字库的数据在 96x64 LCD 上画出来符号。程序也就沿用我为 MCU 写的程序,移植到这里)。 因为没有用中断、DMA操作,到目前程序写法和 Cortex-m MCU 完全一致。为了方便编译,写一个 Makefile
以及 Linker Script, 用来确定地址