boringhex.top博客

非典型程序员的小破站

系列目录

重定向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读取这个地址,然后跳转到我们的启动函数。

阅读全文 »

系列目录

在前一篇文章中我们已经学习到可以通过直接访问存储地址来读写外设寄存器,下面复习下将GPIO A3设为输出模式的代码:

1
2
* (volatile uint32_t *) (0x40020000 + 0) &= ~(3 << 6);  // CLear bit range 6-7
* (volatile uint32_t *) (0x40020000 + 0) |= 1 << 6; // Set bit range 6-7 to 1
阅读全文 »

系列目录

这个系列将介绍STM32裸机编程的基础知识,以便更好地理解STM32Cube、Keil等框架和IDE是如何工作的。本指南完全从头开始,只需要编译器和芯片数据手册,而不依赖任何其它软件工具和框架。

这个系列涵盖了以下话题:

  • 存储和寄存器
  • 中断向量表
  • 启动代码
  • 链接脚本
  • 使用make进行自动化构建
  • GPIO外设和闪烁LED
  • SysTick定时器
  • UART外设和调试输出
  • printf重定向到UART
  • 用Segger Ozone进行调试
  • 系统时钟配置
  • 实现一个带设备仪表盘的web服务器
阅读全文 »

今天在电脑上尝试计算圆周率,把代码和用时贴出来,有兴趣可以在评论区写出你的用时。

阅读全文 »

最近天气越来越冷,在北方南北通透的户型经常面临一个问题就是北面的门窗关不严,呼啸的北风那叫一个凉爽。

原以为是年久失修,门窗里的密封条失去弹性导致密封性能下降。实际上在更换密封条之前还有一个小方法可以试一试。

阅读全文 »
0%