玩了几年的ARM单片机,基本上就在STM32的圈子里;尽管手头囤积的芯片当中还有其它厂商的,都没怎么用,实在惭愧。
昨天拆了一个几年前的手环(因为LP说APP找不到不能用,就当报废了呗),vidonn的产品。这货做得实在是……彻底装不还原了。除了粘合得融为一体之外(面板拆下来都已经裂了),充电接口用的铜件居然是贯穿塑料再焊在PCB上的。
拆了之后再不能戴手上去了。电路部分还是完整拆了出来。
于是,获得带12832OLED屏和电池、加速度传感器、触摸传感器、串行Flash 的 NRF51822 开发板一块。QFN的封装毕竟比BGA还是能容易搞,用表笔可以量引脚。
在屏的那一面还有一些测试点,首先检查 SWDIO, SWCLK 是否引出来了(引出来就不用自己飞线),结论是肯定的。用J-Link测试了SWD可用(顺便把芯片 un-secure 了,也就是擦除已有的程序,就可以任意烧写了)OLED的I2C也连到测试点上了。我对着手册找了一下,直接能用的I/O口就这几个:
Nordic nRF5系列的硬件风格和 STM32 有明显的不同。(题外,国产替代MCU基本还是仿STM32。)比如 GPIO 的寄存器设置和STM32没有兼容性。我先在 J-link commander 里面写了下GPIO的寄存器,将测试点对应的 P0.28, P0.29 设置成输出,然后改变电平。可以控制——说明复位后初试状态GPIO已经使能了。
要给 nRF51822 写程序,必须要有包含寄存器定义、IRQ定义的头文件——这是最重要的。其次还要个启动文件(主要是包含中断向量表的定义),然后还要个GNU Linker的脚本文件(如果没有也可以用其它的比如STM32的改来用,不是那么关键),就可以开撸了。这几个文件在MCU的SDK里面都应该能找到。
我找出来两三年前就下载过的一个 nRF5 SDK 压缩包,从里面把必要的文件提出来。SDK 是支持GCC的,很好。examples 目录里面有 blinky 也就是闪灯的程序,写得很简单。我想先编译下例子呗,不过它是要依赖SDK里面的HAL库的,缺什么文件需要逐个单独拿出来。然而弄下来还是因为缺少开发板定义的一些宏而不能编译,虽然要搞定也不会难。但是这样已经把简单的事情复杂化了,算了,直接操作寄存器吧。
于是就这样写了:
#include <nrf51.h>
int main(void)
{
NRF_GPIO->DIRSET = 1<<28;
while (1)
{
NRF_GPIO->OUTSET = 1<<28;
delay_ms(500);
NRF_GPIO->OUTCLR = 1<<28;
delay_ms(500);
}
}
然后呢,这个 delay_ms() 函数我从写过的 stm32 工程里面找一个,用 SysTick 定时器延时的,直接用。因为要用 nRF51822 的 Timer 的话要先学习,就后面再做。
我把为 STM32 工程写的 Makefile 也移用过来了,用了自己编的通用 Linker 脚本,但是和 nRF SDK 里面的启动汇编文件不兼容,又改了一番启动程序(代码都被我换了,就借用了它的中断向量表。想到这,日后我再写个启动程序生成工具得了)。
然后编译完成,用nm命令看了一下ELF里面地址分布没有问题了,就用J-link OB来烧写。
我接在 GPIO0.28 的LED亮了,但是不闪烁,奇怪了。于是GDB进去看,发现在 delay_ms() 函数里面没有返回。怪了,SysTick 是 cortex-m0 都通用的操作方法,怎么会挂了呢?看了反汇编代码我也没有发现异样。再从头单步跟踪下,执行过程也没有问题,不然LED不会亮的。查看对SysTick寄存器的写指令,也是正确的。
然而我查看了一下写 SysTick 寄存器之后的值,竟然都是 0. 这没写进去也不可能啊。于是网上搜索一下,结果是 nRF51 系列并没有配置 SysTick 模块。这个在 cortex-m0 中的确是可选的,只不过一般MCU都有而已。
那好吧,暂时就用一长串 NOP 指令来改写 delay_ms() 函数了。
void delay_ms(uint16_t t)
{
int i; // no Systick timer in NRF51 series
for(i=0;i<t;i++)
{
int m;
for(m=0;m<10;m++)
{
asm volatile ("nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n"
"nop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\nnop;\n");
}
}
}
再编译,烧进去,reset, LED就闪了。用示波器测量闪烁频率,推算出CPU时钟在16MHz,这是复位后默认的时钟。
|