boringhex.top博客

非典型程序员的小破站

系列目录

带设备仪表盘的网络服务器

Nucleo-F429ZI 带有板载以太网。以太网硬件需要两个组件:PHY(向铜缆、光缆等介质发送和接收电信号)和 MAC(驱动 PHY 控制器)。
在我们的Nucleo开发板上,MAC控制器是MCU内置的,PHY是外部的(具体来说,是Microchip的LAN8720a)。

MAC和PHY可以用多个接口通信,我们将使用RMII。为此,一些引脚必须配置为使用其替代功能 (AF)。要实现 Web 服务器,我们需要 3 个软件组件:

  • 网络驱动程序,用于向 MAC 控制器发送/接收以太网帧
  • 一个网络堆栈,用于解析帧并理解 TCP/IP
  • 理解HTTP的网络库

我们将使用猫鼬网络库,它在单个文件中实现所有这些。这是一个双重许可的库(GPLv2/商业),旨在使网络嵌入式开发快速简便。

阅读全文 »

系列目录

配置时钟

启动后,Nucleo-F429ZI CPU以16MHz运行,最大频率为180MHz。请注意,系统时钟频率并不是我们需要关心的唯一因素。外设连接到不同的总线,APB1 和 APB2 时钟不同。 它们的时钟速度由频率预分频器配置值,在 RCC 中设置。主 CPU 时钟源也可以不同 - 我们可以使用外部晶体振荡器 (HSE) 或内部振荡器(HSI)。在我们的例子中,我们将使用 HSI。

当CPU从闪存执行指令时,闪存读取速度(大约25MHz)在CPU时钟变高时成为瓶颈。有几个技巧会有所帮助,指令预取就是其中之一。此外,我们可以给闪存控制器提供一些线索,告诉它系统时钟有多快:该值称为闪存延迟。对于 180MHz 系统时钟,FLASH_LATENCY值为 5。闪存控制器中的位 8 和 9 控制启用指令和数据缓存:

1
FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9);      // Flash latency, caches
阅读全文 »

系列目录

供应商CMSIS头文件

在前面的部分,我们仅使用数据手册、编辑器和GCC编译器开发了固件程序,使用数据手册创建了外设结构定义。

现在我们已经知道MCU是怎么工作的,是时候介绍一下CMSIS头文件了。它是什么?它是由MCU厂商创建和提供的带有全部定义的头文件。它包含MCU相关的全部,所以很庞大。

CMSIS代表通用微控制器软件接口标准(Common Microcontroller Software Interface Standard),因此它是MCU制造商指定外设API的共同基础。 因为CMSIS是一种ARM标准,并且CMSIS头文件由MCU厂商提供,所以是权威的来源。因此,使用供应商头文件是首选方法,而不是手动编写定义。

在这一节,我们将使用供应商CMSIS头文件替换 mcu.h 中的API函数,并保持固件其它部分不变。

阅读全文 »

系列目录

用Segger Ozone进行调试

如果我们的固件卡在某个地方并且 printf 调试不起作用怎么办?甚至连启动代码都不起作用怎么办?我们需要一个调试器。那有很多选项,但我建议使用Segger的Ozone调试器。为什么?因为它是独立的,不依赖任何IDE。我们可以把 firmware.elf 直接提供给Ozone,它会自动拾取源文件。

可以从Segger网站下载 Ozone。在用它调试我们的Nucleo开发板之前,我们需要把板载的ST-LINK固件改成jlink的固件,这样Ozone才能识别。遵循Segger网站的说明完成固件修改。

阅读全文 »

系列目录

重定向printf()到串口

在这一节,我们将 uart_write_buf() 调用替换为 printf(),它使我们能够进行格式化输出,这样可以更好的输出诊断信息,实现了“打印样式的调试”。

我们使用的GNU ARM工具链除了包含GCC编译器和一些工具外,还包含了一个被称为newlib的C库,由红帽为嵌入式系统开发。

如果我们的固件调用了一个标准C库函数,比如 strcmp(),newlib就会被GCC链接器加到我们的固件中。

newlib实现了一些标准C函数,特别是文件输入输出操作,并且被实现的很随潮流:这些函数最终调用一组被称为 “syscalls” 的底层输入输出函数。

例如:

  • fopen() 最终调用 _open()
  • fread() 最终调用 _read()
  • fwrite(), fprintf(), printf() 最终调用 _write()
  • malloc 最终调用 _sbrk(),等等
阅读全文 »

系列目录

添加串口调试输出

现在是时候给固件添加一些人类可读的诊断信息了。MCU外设中有一个串行通信接口,通常被称作串口。看一下芯片数据手册2.3节,STM32F429有多个串口控制器,适当配置后就可以通过特定引脚与外部交换数据。最小化的串口配置需要2个引脚,一个接收,另一个发送。

在Nucleo开发板数据手册6.9节,可以看到MCU的串口3的发送引脚是PD8,接收引脚是PD9,并且已经被连到了板载的ST-LINK调试器上,这意味着我们配置好串口3就可以通过PD8发送数据,然后通过ST-LINK在工作站上看到MCU发送的数据。

现在给串口创建API,就像之前GPIO那样。芯片数据手册30.6节概括了串口寄存器,可以这样定义串口结构体:

1
2
3
4
5
6
struct uart {
volatile uint32_t SR, DR, BRR, CR1, CR2, CR3, GTPR;
};
#define UART1 ((struct uart *) 0x40011000)
#define UART2 ((struct uart *) 0x40004400)
#define UART3 ((struct uart *) 0x40004800)
阅读全文 »

系列目录

用SysTick中断实现闪烁

为了实现精确的时间控制,我们应该使能ARM的SysTick中断。SysTick是一个24位的硬件计数器,是ARM核的一部分,因为在ARM的数据手册中有它的文档。从芯片数据手册中可以看到,SysTick有4个寄存器:

  • CTRL,使能/禁能SysTick
  • LOAD,初始计数值
  • VAL,当前计数值,每个时钟周期递减
  • CALIB,校准寄存器

每次VAL减到0,就会产生一个SysTick中断,SysTick中断在向量表中的索引为15,我们需要设置它。在启动时,Nucleo-F429ZI的时钟是16MHz,我们可以配置SysTick计数器使其每毫秒产生一个中断。

阅读全文 »

系列目录

闪烁LED

现在我们已经搭建好了完整的构建、烧写的基础设施,是时候让固件做点儿有用的事情了。什么是有用的事情?当然是闪烁LED了!Nucleo-F429ZI开发板有3颗LED,在开发板数据手册的6.5节,我们可以看到板载LED连接的引脚:

  • PB0: green LED
  • PB7: blue LED
  • PB14: red LED
阅读全文 »

系列目录

Makefile:构建自动化

我们可以用 make 命令行工具替代手动敲入“编译”、“链接”、“烧写”这些命令,自动完成整个过程。make 工具使用一个名为 Makefile 的配置文件,从中读取执行动作的指令。这种自动化方式非常棒,因为这样可以把构建固件的过程、使用了哪些编译标记等也文档化。

https://makefiletutorial.com 上有一个非常好的给初学者的Makefile教程,强烈建议看一下。下面我将列出一些非常必要的概念以理解我们所使用的Makefile。对于已经很熟悉 make 的朋友,可以跳过这一部分。

其实 Makefile 的格式并不复杂:

1
2
3
4
5
6
action1:
command ... # Comments can go after hash symbol
command .... # IMPORTANT: command must be preceded with the TAB character

action2:
command ... # Don't forget about TAB. Spaces won't work!
阅读全文 »

系列目录

MCU启动和向量表

当STM32F429 MCU启动时,它会从flash存储区最前面的位置读取一个叫作“向量表”的东西。“向量表”的概念所有ARM MCU都通用,它是一个包含32位中断处理程序地址的数组。对于所有ARM MCU,向量表前16个地址由ARM保留,其余的作为外设中断处理程序入口,由MCU厂商定义。越简单的MCU中断处理程序入口越少,越复杂的MCU中断处理程序入口则会更多。

STM32F429的向量表在数据手册表62中描述,我们可以看到它在16个ARM保留的标准中断处理程序入口外还有91个外设中断处理程序入口。

在向量表中,我们当前对前两个入口点比较感兴趣,它们在MCU启动过程中扮演了关键角色。这两个值是:初始堆栈指针和执行启动函数的地址(固件程序入口点)。

所以现在我们知道,我们必须确保固件中第2个32位值包含启动函数的地址,当MCU启动时,它会从flash读取这个地址,然后跳转到我们的启动函数。

阅读全文 »
0%