CC3220SF开发板到货,拍照留念
两兄弟,右边这块是CC3220SF,大一些。
CC3220到货,先搞个类似51单片机的程序,直接控制寄存器来点亮LED。
先查阅CC3220技术参考手册,的第五章【General-Purpose Input/Outputs (GPIOs)】介绍了GPIO的相关内容。
5.4 初始化和配置
配置指定端口的GPIO引脚:
- 通过设置 GPIO0CLKEN、c、GPIO2CLKEN、GPIO3CLKEN和GPIO4CLKEN寄存器中合适的位来使能指定port的时钟。
- 通过GPIODIR寄存器编程设置GPIO port引脚的方向。置1表示输出,置0表示输入。
查找三盏灯所接引脚:
CC3220的引脚被分为4组,分别是为Port A0、Port A1、Port A2、Port A3。首先我们找到CC3220SF开发板的三盏LED灯所对就的GPIO口,查电路图:
看看实物图:
D8 :绿灯,接GPIO_11
D9 :黄灯,接GPIO_10
D10:红灯,接GPIO_9
查找三盏灯所接GPIO口所对应的port
还是同一章,找到GPIO映射表:
D8 :绿灯,接GPIO_11 ---GPIOA1 的第3位引脚
D9 :黄灯,接GPIO_10 ---GPIOA1 的第2位引脚
D10:红灯,接GPIO_9 ---GPIOA1 的第1位引脚(从0开始)
也就是说,三盏灯同属GPIOA1,那么,我们只需打开GPIO1CLKEN即可。
寻找GPIO1CLKEN寄存器地址
首先寻找时钟管理的基地址,【2.2.3 Memory Model】这一节的【Table 2-4 Memory Map】中可以找到时钟管理基地址,如图:
找到时钟管理基地址,0x4402.5000
接下来【15.6.16 GPIO1CLKEN Register】
DSLPCLKEN:深度睡眠
SLPCLKEN:睡眠
RUNCLKEN:正常运行
由图可看到偏移量offset = 58h,重启默认值reset = 0h。我们需要的是正常运行状态,也就是把RUNCLKEN置1即可打开GPIOA1时钟。
最终结果:[0x44025000+0x58]寄存器值设为0x01
寻找GPIODIR寄存器地址
翻到【5.5 GPIO Registers】,看到以下内容:
前面已经说过,CC3220把所有GPIO分为4组,A0~A3,上面列出了每个组的基地址。而每个组都有自己的寄存器。Table 5-3列出的是每组自身所拥有的寄存器,Offset列就是各组寄存器针对各组基地址位移。
上面特意声明,在这些寄存器可以访问前,每个GPIO的模块时钟必须首先开启,在开启后的延时3个系统时钟方能访问这些寄存器。
接下来点GPIODIR寄存器右边链接转到5.5.2节,如下图:
前面我们看过Table 5-14 GPIO Mapping,可观察到 4组GPIO,每组管8个引脚。而GPIODIR寄存器的DIP字段正好占8个位,每个位对应一个引脚。那么根据前面所查资料,可以得知三盏LED由上图红框的3个位所控制。只要把它们都置1,三盏LED的引脚都会变为输出方向。
那么GPIO Port A1基地址0x40005000,GPIODIR寄存器地址偏移400h。得出结论
最终结果:[0x40005000+0x400] = 0x0E
引脚复用
相比51单片机,CC3220是一块高级芯片,大部分引脚经过配置,都可以实现不同的功能,如I2C、SPI、UART或GPIO。接下来我们就需要将3盏LED所连接的引脚配置为GPIO。
首先翻到【16.7 Functional Pin Mux Configurations】,找到GPIO9、GPIO10、GPIO11三个引脚复用数据,如下图:
GPIO9:
GPIO10、GPIO11:
注意红色方框标注处。可注意到,只要将三个引脚所对应的寄存器名称和地址分别为:
GPIO_PAD_CONFIG_9 :0x4402E0C4
GPIO_PAD_CONFIG_10 :0x4402E0C8
GPIO_PAD_CONFIG_11 :0x4402E0CC
只需将它们的ConfigMode配置为0,即可用为GPIO。
接下来翻到【16.8.1.1 Pad Mux and Electrical Configuration Register Bit Definitions】,如下图:
GPIO_PAD_CONFIG_x寄存器占用12个位。[3:0]用于ConfigMode,如上所述,这里设为0就是GPIO。[7:5]用于驱动强度,我们把它设为001 = 2mA即可。[3:0]=0,[7:5]=001,得出数字0x20。
最终结果:
[0x4402E0C4] = 0x20
[0x4402E0C8] = 0x20
[0x4402E0CC] = 0x20
控制引脚高低电平
不要急,还没完。配置是结束了,点亮或熄灭LED灯还需控制GPIO的高低电平,这是一个非常复杂难懂的过程。
还是翻到【5.5 GPIO Registers】看Table 5-3的内容,
如上表所示,GPIODATA的地址范围从0h~3FFh,总共占用1024个字节。而每组GPIO共8个引脚,一个字节即可表示完成。下面看看关于GPIODATA寄存器的描述,翻到【5.5.1 GPIODATA Register (offset = 0h) [reset = 0h]】,图5-4:
考虑到MCU为32位总线,那么需要4个字节来控制一组GPIO口(只使用了前8位)。为何留了1024个字节来控制8个引脚呢?
实际上CC3220将这1024个字节分为256个区域(即256个别名,1024/4=256),每个区域正好存放控制一组GPIO口所需的32个位。第一个区地址偏移量为0x00,第二个区地址偏移量为0x04,最后一个区地址偏移量为0x3FC。在不同的区域读写GPIO得到的是不同的效果。翻到【5.2.1.2 Data Register Operation】。
1、先看看写操作
如上图所示:
第一行方块表示地址偏移量
第二行方块表示要写的值
第三行方块表示GPIODATA的结果值
在偏移量为0x098这个地址上写GPIO。0x098的第2到第9位可作为写操作掩码,把0x098用二进制来看,3、4、7位的值为1,表示对这三位的写操作可以成功。剩下的5位对它们进行任何写操作都不会改变GPIODATA的值。
换句话说,如果想用一个数字同时操作一组GPIO的8个引脚,就应当在偏移量为0x3FC的地址上写。如果在偏移量为0x000的地址上写,则不会有任何效果。
2、再来看读操作
如上图所示:
第一行方块表示地址偏移量
第二行方块表示GPIODATA的当前值
第三行方块表示读操作返回值
上图的地址偏移量为0x04C,表示只会返回GPIODATA中第2、6、7这三个位的结果,其它位的值不会返回。
这样做的好处翻译一下:软件驱动可在一个单周期指令内完成对单个GPIO引脚的修改,而不影响其它引脚的状态。相对于传统方法的读-改-写操作去设置或清除单个GPIO引脚,此方法更具效率。
下面可以推断出,三盏LED所在GPIO Port A1基地址0x40005000;我们打算象51单片机那样同时控制3盏灯,3盏灯处于A口的第1、2、3位。为不影响其它GPIO口,偏移量应设计为二进制的(0000111000),即16进制的0x038。
最终结果:[0x40005000 + 0x00E] = xx
好,该讲的讲完了,不容易啊!下面可以写程序了。这回写最底层的象51单片机一样的程序。
例一:三盏灯同时闪烁:
首先单击菜单【Project】-->【New CCS Project】,弹出新建项目窗体,画红框的地方按图进行设置,Project name可以自己起。
接下来在自动生成的main.c文件输入如下代码:
//延时函数
void delay(int temp)
{int i = 0;for (i = 0; i < temp; i++);
}int main(void)
{//开启GPIOA1时钟*((volatile unsigned long *)(0x44025000 + 0x58)) = 0x01;//设置3个LED引脚为输出方向*((volatile unsigned long *)(0x40005000 + 0x400)) |= 0x0E;//配置3个LED引脚为GPIO,电流强度为2mA*((volatile unsigned long *)(0x4402E0C4)) = 0x20;*((volatile unsigned long *)(0x4402E0C8)) = 0x20;*((volatile unsigned long *)(0x4402E0CC)) = 0x20;while(1){ //同时点亮3盏LED*((volatile unsigned long *)(0x40005000 + 0x038)) = 0x0E;delay(0x2fffff);//同时熄灭3盏LED*((volatile unsigned long *)(0x40005000 + 0x038)) = 0x00;delay(0x2fffff);}
}
这程序牛啊,未引用任何头文件!
单击绿色甲壳虫按钮(Debug),烧进开发板后按绿色三角(Resume)按钮,观察开发板,三盏灯同时闪烁。大功告成,走到这一步不容易啊!
心情好,再来个跑马灯:
void delay(int temp)
{int i = 0;for (i = 0; i < temp; i++);
}int main(void)
{//开启GPIOA1时钟*((volatile unsigned long *)(0x44025000 + 0x58)) = 0x01;//设置3个LED引脚为输出方向*((volatile unsigned long *)(0x40005000 + 0x400)) |= 0x0E;//配置3个LED引脚为GPIO,电流强度为2mA*((volatile unsigned long *)(0x4402E0C4)) = 0x20;*((volatile unsigned long *)(0x4402E0C8)) = 0x20;*((volatile unsigned long *)(0x4402E0CC)) = 0x20;int flag = 2;while(1){*((volatile unsigned long *)(0x40005000 + 0x038)) = flag;flag = (flag == 8) ? 2 : flag << 1;delay(0x2fffff);}
}
本文参考了
阿汤哥的文章
,没他的文章我是肯定没办法写出这程序的,有部分内容他没讲清楚,我这也算是补全了吧。