第三课:Hello, World!(AT91SAM3S微控制器IO口编程初探)
从最早接触的8051单片机,汇编开发,之后用过PIC16、PIC18、PIC24、MSP430、LPC2138,到现在用的最多的STM32和S3C2440,及最近开始熟悉AT91SAM3S和TI AM3359。我无论刚开始接触哪一类MCU(MPU),一定要做到的就是从点亮一盏LED灯开始,始终坚信,只要能点亮LED灯,就算迈进了这款新MCU(MPU)世界的大门。这就是硬件开发中的Hello,world!
所以这节课就是在MDK环境下,基于悠龙提供的SAM3S-EK开发板上完成点亮一盏LED的实验。
注意:由于ARM的开发比51、MSP430、PIC8位单片机复杂一些,所以不再一点点从建项目开始说,要不然这一课的写上万字。站的角度是你至少会用MDK,至少玩过一种其他的基于ARM核心的微控制器(这个要求其实不高,很多童鞋已经比较熟悉STM32了),如果没有接触过请加我QQ9790335,我给你入门资料。
一、模仿是一切创新的基石
刚拿到开发板,直接看datasheet,发现根本没法进行下去,有种老虎吃天,无从下口的感觉,所以拿到一个新的开发板,最好的入门就是赶紧看官方的demo板子的例程,所以今天我们先来剖析一下官方的例子。
在悠龙提供的光盘里有个文件夹:uTenxu_AT91SAM3S\Software\sample,下面有一个mdk_source.zip压缩包,这就是例程。
解压之后在里面找一个08-Led_Controller文件夹,下面的项目就是“点亮LED灯”的项目,所谓的“Hello,world!”就是他了。我们把它拷贝出来放到一个自己建立的干净的文件夹下,便于分析文件系统,我放在F:\ER\AT91SAM3S4C\01-codeExample下面。以下以此目录为例。
然后双击F:\ER\AT91SAM3S4C\01-codeExample\08-Led_Controller\project下的SAM3S4C.uvproj打开项目。
我们试图编译一下这个项目,看有什么现象,只有编译通过才说明我们没有遗漏文件,这也是为什么我把这个项目拷贝到一个干净的目录下的原因。编译之后如图1所示。
我们发现编译没有成功,出错了,除了警告之外错误大部分是类似“..\..\common\peripherals\source\wm8731.c(101): error: #20: identifier "WM8731_REG_RESET" is undefined”这样的,意思是说找不见某某文件。(这里“..\”是上一级目录的意思)于是我们发现一个问题,在这个项目往上找两级目录,我们没有找到common这个文件夹,自然这个文件夹下的文件是找不到的,这说明我们遗漏有文件。
仔细观察一下官方的这个例子,我们发下它加入了好多文件,在MDK左侧的项目树at91lib节点下有很多adc、spi、pio……很多文件,从名字上我们也能看出,这是官方给每个模块写的库文件,类似STM32的“标准外设固件库”,找不见的文件都是这个目录下的,说明官方的库放在common文件夹下。
问题明确了,我们再次回到解压的那个目录下,果然发现,在这个08-Led_Controller文件夹同一级(也就是SAM3S4C.uvproj文件往上数两级----两个..\),找到一个common文件夹,我们把它照搬到F:\ER\AT91SAM3S4C\01-codeExample下。再次编译,发现编译成功!除了两个警告(warnings),这两个警告暂且不要管,起码项目是编译通过的。
说明我们完整的获得了一个参考例程,我们将这个例程下载到我们的开发板上,看看效果!(如果你使用的是H-Jtag不用改任何东西,只要打开H-Jtag Server即可。如果你使用的Jlink或者ULink,需要修改一下Debug调试器的类型。详细请参与第二课)。
然后我们点击Debug按钮,全速执行程序,发现板子上的蓝/绿两盏灯交替闪烁。我们完成了第一步,保证了参考程序的完整性,验证了官方程序的正确性。下一步我们开始分析这个例程。
二、知己知彼
(1)C宏定义:sam3s4
(2)C包含路径
..\..\common\peripherals;
..\inc;
..\project;
..\..\common\chip;
..\..\common;
..\..\common\usb\include;
..\..\common\chip\include
(3)Misc Control: --gnu
(4)Linker:
去掉Use Memory Layout from Target Dialog 前面的勾,增加Scatter file: ..\common\flash.sct
(5)Debug的初始化文件
..\..\common\sam3s-ek-flash.ini
3.项目组织结构 文件多了我们需要分组,尤其适用库函数的时候,我们点开“Components、Evironment and books”,查看分组情况,总结如下。
(1)startup--common\workaround.s
(2)at91lib--(官方在不同的项目中没有包涵完,这个可以理解,没有使用的外设我们没有必要全包涵进来,会白白增加编译时间)
common\peripherals\source
common\chip\source\
(3)usr--用户自己的程序(main.c、……)
(4)ReadMe--ReadMe.txt(关于项目的说明)
本小节解惑:
1. 有同学要说,为什么要扒这些东西?
不知道项目配置就不知道哪些文件是必要的,而且刚开始很多稀奇古怪的问题都是配置引起的,所以扒这个是为了后面少麻烦。
2. 我怎么知道上面这些配置是原来就有的,还是官方加的?
好办,自己建一个空的项目,然后一项项对比-_-#,刚开始不要嫌麻烦,坚持住!
三、照猫画虎(法宝在手,代码不愁)--第一个项目,点亮一盏LED灯
│
├─common
│ ├─chip
│ │ ├─cmsis
│ │ ├─include
│ │ └─source
│ ├─fat
│ ├─LIB
│ ├─memories
│ │ ├─build
│ │ │ └─mdk
│ │ ├─include
│ │ ├─nandflash
│ │ ├─norflash
│ │ ├─sdmmc
│ │ └─spi-flash
│ ├─peripherals
│ │ ├─include
│ │ └─source
│ ├─startup
│ └─usb
│ ├─common
│ │ ├─audio
│ │ ├─cdc
│ │ ├─core
│ │ └─hid
│ ├─device
│ │ ├─audio-speaker
│ │ ├─audio-speakerphone
│ │ ├─ccid
│ │ ├─cdc-serial
│ │ │ └─drv
│ │ ├─composite
│ │ │ └─drv
│ │ ├─core
│ │ ├─hid-keyboard
│ │ ├─hid-mouse
│ │ ├─hid-transfer
│ │ └─massstorage
│ └─include
├─inc
├─project
│ ├─flash
│ └─sram
├─ReadMe
└─src
注意:
(1)这里做了一点小小的修改,官方把commom放到项目文件夹外边了,然后采用文件包含的形式来引用,这样便于多个项目同时共享这个库,但是也有个不方便的地方,就是我们的库不独立。为了每个项目相对独立,这里我把他放到项目文件夹的下面。好处我们后面可以看到,这里为了不增加理解的难度,暂且不表,这么搞就行了。
(2)project下有两个文件夹flash和sram暂且不要管,反正刚开始也看不懂,以后再去研究。(可以大胆猜猜,留在后面我再说^^)
2. 拷贝官方的库文件common到工程目录下(上面已经做了这一步)
3. 在MDK中建立项目(略过),按照官方的配置,如上面的第二部分讲的那样,配置项目,这里引用库的时候请引用01-ToggleIO里面的这个库。
4. 打开官方的项目,开始照猫画虎。(该亮法宝了!---“Go To Difinition of 'xxx'”)
我们看官方的文件从哪儿开始看?第一行?往下看?No,No……这样就挂了!从main函数开始看
官方main函数我的分析如下,刚开始要大胆的猜,错了我们再撤销就行了:
extern int main( void )
{
/* 这两句好象是说对于Keil µVision 4做了点额外的工作,暂且保留 */
#if defined ( __CC_ARM ) /* Keil µVision 4 */
/* Disable semihosting */
# pragma import(__use_no_semihosting_swi)
#endif
/* Disable watchdog(关闭看门狗,大家都认识,保留!从这里我们获得到一个额外的信息-->AT91SAM3S上电默认是开着看门狗的) */
WDT_Disable( WDT ) ;
/* Output example information(从printf可以看出这个是想通过串口输出信息给用户的,我们做的是Hello,world!实验,就点个灯,为了给自己减小难度,删掉!)*/
printf( "-- Getting Started Example %s --\n\r", SOFTPACK_VERSION ) ;
printf( "-- %s\n\r", BOARD_NAME ) ;
printf( "-- Compiled: %s %s --\n\r", __DATE__, __TIME__ ) ;
/* Configure systick for 1 ms. (这个是在配置Systick定时器设置1ms延时的,得要!保留!一会儿我们的灯要闪烁。)*/
/* 这个配置有提示用户的容错功能,我们刚开始用不到,我暂且相信悠龙给的这个板子没有问题,是好的,刚才也测试了确实是好的,所以不用容错测试,我们将本句改一改。
while(SysTick_Config( BOARD_MCK / 1000 ) != 0);
这句不用我说了吧?都能看懂,就是你要是配不好,我就在这里死等。这里有两个东西可能让人晕,这个BOARD_MCK是个神马东西?
printf( "Configure system tick to get 1ms tick period.\n\r" ) ; //这个就没必要了,删掉!
if ( SysTick_Config( BOARD_MCK / 1000 ) )
{
printf("-F- Systick configuration error\n\r" ) ;
}
/* PIO configuration for LEDs and Buttons. (初始化LED和Buttons,LED我们用的到,Buttons先算了,上法宝找到这个函数,在pio_it.c中,原来是配置中断,我们用不着,删掉!)*/
PIO_InitializeInterrupts( IRQ_PRIOR_PIO ) ;
/* 还是法宝,找到_ConfigureTc函数,看解释,原来是配置定时器0的,我们显然用不到,大大方方的删! */
printf( "Configure TC.\n\r" ); //根据上面描述,这一句铁定要删除的
_ConfigureTc() ;
/* 法宝真的很管用-_-# 找到_ConfigureLeds函数,这里有惊喜!看介绍“Configure LEDs”,这个是我们要用的,我们点过去看看。
static void _ConfigureLeds( void )
{
LED_Configure( 0 ) ; //发现这里又是一个不认识玩意儿,不怕!我们继续“Go To Difinition of 'LED_Configure'”
LED_Configure( 1 ) ;
}
在LED.c中我们找到了LED_Configure函数,如下:
extern uint32_t LED_Configure( uint32_t dwLed )
{
#ifdef PINS_LEDS
// Check that LED exists(一看就是容错机制,不用管,我们大方删掉!)
if ( dwLed >= numLeds)
{
return 0;
}
// Configure LED
return ( PIO_Configure( &pinsLeds[dwLed], 1 ) ) ; //只剩下这一句是硬货!我们有法宝,继续上绝招,找PIO_Configure函数
#else
return 0 ;
#endif
}
又扒了一层,在pio.c中我们找到了PIO_Configure函数,太长,不写了,请大家自己看,而且函数有解释,其实是配置一个IO口的函数。
想想也是,点灯不就是把一个IO口配置成推挽输出模式,然后一会儿给1,一会儿给0么?有点眉目了。
函数原型是uint8_t PIO_Configure( const Pin *list, uint32_t size )。
第1个参数是一个Pin类型的指针类型,有同学又蒙了,这个Pin又是啥玩意儿?别蒙,我们有法宝!上绝招我们找到了Pin的定义。
/*
* Describes the type and attribute of one PIO pin or a group of similar pins.
* The #type# field can have the following values:
* - PIO_PERIPH_A
* - PIO_PERIPH_B
* - PIO_OUTPUT_0
* - PIO_OUTPUT_1
* - PIO_INPUT
*
* The #attribute# field is a bitmask that can either be set to PIO_DEFAULt,
* or combine (using bitwise OR '|') any number of the following constants:
* - PIO_PULLUP
* - PIO_DEGLITCH
* - PIO_DEBOUNCE
* - PIO_OPENDRAIN
* - PIO_IT_LOW_LEVEL
* - PIO_IT_HIGH_LEVEL
* - PIO_IT_FALL_EDGE
* - PIO_IT_RISE_EDGE
*/
typedef struct _Pin
{
/* Bitmask indicating which pin(s) to configure. */
uint32_t mask;
/* Pointer to the PIO controller which has the pin(s). */
Pio *pio;
/* Peripheral ID of the PIO controller which has the pin(s). */
uint8_t id;
/* Pin type. */
uint8_t type;
/* Pin attribute. */
uint8_t attribute;
} Pin ;
看到这儿我都不想解释了。人家说的多详细啊!
可以看到type和attribute的值不用我们操心了按照上面的配置即可,Pio我们可以使用绝招找到。
但是有的同学又蒙了,mask/id又是什么?这里发现法宝不好使了。。。没法再继续了。。。别慌,有办法。我们有官方例程怕什么呢?
官方例程中PIO_Configure( &pinsLeds[dwLed], 1 )中我们找到pinsLeds看看又是什么呢?这个不就是Pin类型么?
上法宝我们找到了static const Pin pinsLeds[] = { PINS_LEDS } ;还没有到头,继续,我们又找到了#define PINS_LEDS PIN_LED_0, PIN_LED_1, PIN_LED_2
还没有到头,继续!
终于!妈的,藏的够深的啊,找到了这个#define PIN_LED_0 {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
我们对比一下,猜都能猜出这是PA19这个端口的配置,对照板子原理图,它是我们的D2,蓝灯。
没有白找,现在我们总结一下收获:
(1)Pin数据类型是 {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}这种结构
我们照猫画虎啊如果是PB10应该是什么样的呢?{PIO_PB10, PIOB, ID_PIOB, 默认输出, 配置参数},猜出来这样的。就对了!
(2)PIO_Configure( const Pin *list, uint32_t size )函数用于配置IO口,使用PIO_Configure((在代码中定义的Pin类型的数组首地址),这个数组中定义的IO个数)
Const类型说明指向的是Flash区域,而非Ram,说明是写在代码里面的。这里也解释了配置端口可以一次性配置好多,写成数组的形式。
size是指要初始化的IO口的个数,因为编译器是不知道你这个list到底有多长。这个个数要注意,可以少于这个数组中定义的个数,但是不能多,为什么呢?自己想。。。
如果我懒得算这个数,怎么办?官方有个小工具PIO_LISTSIZE(数组名),就能自动算出你指定的这个数组中定义了几个IO。详细自己找,算是课后作业吧。
(3)这里学会的是方法,不是为了找这几个小破函数,我们的绝招是我们学习的法宝!时刻记住,我们有法宝!
*/
printf( "Configure LED PIOs.\n\r" ) ; //根据上面描述,这一句铁定要删除的
_ConfigureLeds() ; //这个扒的最长,见上面,法宝已经亮出,下面的我不打算再分析了,作为作业,请大家自行分析*/
printf( "Configure buttons with debouncing.\n\r" ) ; //根据上面描述,这一句铁定要删除的
_ConfigureButtons() ; //我们不用buttons,删除! 建议大家有空看看,也是配置IO口,跟上面LED很像,但这个是配置输入的
printf( "Press USRBP1 to Start/Stop the blue LED D2 blinking.\n\r" ) ;//删除
printf( "Press USRBP2 to Start/Stop the green LED D3 blinking.\n\r" ) ;//删除
while ( 1 )
{
/* Wait for LED to be active (作业,请自己扒) */
while( !bLed0Active );
/* Toggle LED state if active */
if ( bLed0Active )
{
LED_Toggle( 0 ); //重点扒这一句,能够扒出一个PIO_Clear函数用于把一个IO口输出0,PIO_Set函数用于把一个IO置1
printf( "1 " );
}
/* Wait for 500ms */
_Wait(500); //作业,自己扒,法宝在手,代码不愁!
}
}
通过上面的分析,我写出下面的一个简单的程序---Hello, world!,供大家参考,所用的函数都是上面用法宝扒过的。新加的内容我有解释。
5. 新建main.c文件,把下面的内容粘贴过去:
/* 包含文件:官方这么包含的,我们就先这么包含,别总想着往里面看,先把现象搞出来,再去试着改 */
#include "board.h"
#include /* 等这个程序可以正常运行的时候,我们试着去掉stdbool和stdio头文件,看有没有影响,现在先留着,保持文件的完整性。程序运行后我们删掉这两个包含文件发现没有影响,那说明:可以删!不过这个过程希望大家自己尝试,不要光听我的 */
#include
/* D2(蓝灯)PA19,推挽输出模式 */
static const Pin LED_2 = {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT};
/*****************************************************************************
* Local functions
*/
/*----------------------------------------------------------------------------
* function name: SysTick_Handler
* parameter : void
* return : none
* brief : Handler for SysTick interrupt. Increments the timestamp counter.
*/
void SysTick_Handler( void )
{
TimeTick_Increment() ;
}
/*****************************************************************************
* Global main
* Application entry point for smc_lcd example.
* return Unused (ANSI-C compatibility).
*/
extern int main( void )
{
/* local variables */
uint8_t i=0;
/* Disable watchdog */
WDT_Disable( WDT ) ;
/* Configure systick for 1 ms. */
while(SysTick_Config( BOARD_MCK / 1000 )!= 0);
/* Configure & initialize the PIO */
PIO_Configure(&LED_2, 1);
/* Infinate circle */
while(1)
{
PIO_Set(&LED_2); //turn off D2
Wait(500); //waiting for 500 ms
PIO_Clear(&LED_2); //turn off D2
Wait(500); //waiting for 500 ms
}
}
6. 只要是按照官方的配置配的项目和项目分组,只修改着一个main.c文件,编译就能通过,下载到板子上发现D2蓝色的灯就会以500ms交替闪烁。
四、今天迈出一小步,是我们进入AT91SAM3S世界的一大步!
本节只要能对照着官方例程建项目、项目分组即可,不用死记硬背,这些熟练了自然就记住了。本节重点是会使用“法宝”探索新例程(这是重点!)只要这种精神存在,搞定官方案例只是时间的问题。
始终记住:模仿是创新的基石。不要一上来就想改很多东西,这样只能给自己增加难度,改出问题了只会打击自己的信心。知己知彼,才能百战百胜,先搞定官方的心思。
敲字真的很累,语言表达真的胜过文字,我上课的时候这些内容50分钟就能说完,敲字敲了2小时,还忽略了一些基本的操作,打算后面的课程录视频。如果对我的描述有不清楚的地方欢迎跟贴提出,或者QQ我。下一节我们来对这个案例进行丰富,增加按键的功能,下节继续!March on!
写在前面:
从最早接触的8051单片机,汇编开发,之后用过PIC16、PIC18、PIC24、MSP430、LPC2138,到现在用的最多的STM32和S3C2440,及最近开始熟悉AT91SAM3S和TI AM3359。我无论刚开始接触哪一类MCU(MPU),一定要做到的就是从点亮一盏LED灯开始,始终坚信,只要能点亮LED灯,就算迈进了这款新MCU(MPU)世界的大门。这就是硬件开发中的Hello,world!
所以这节课就是在MDK环境下,基于悠龙提供的SAM3S-EK开发板上完成点亮一盏LED的实验。
注意:由于ARM的开发比51、MSP430、PIC8位单片机复杂一些,所以不再一点点从建项目开始说,要不然这一课的写上万字。站的角度是你至少会用MDK,至少玩过一种其他的基于ARM核心的微控制器(这个要求其实不高,很多童鞋已经比较熟悉STM32了),如果没有接触过请加我QQ9790335,我给你入门资料。
一、模仿是一切创新的基石
这一步我们要做的是:保持文件的完整性,保证我们要参考的例程是对的!
刚拿到开发板,直接看datasheet,发现根本没法进行下去,有种老虎吃天,无从下口的感觉,所以拿到一个新的开发板,最好的入门就是赶紧看官方的demo板子的例程,所以今天我们先来剖析一下官方的例子。
在悠龙提供的光盘里有个文件夹:uTenxu_AT91SAM3S\Software\sample,下面有一个mdk_source.zip压缩包,这就是例程。
解压之后在里面找一个08-Led_Controller文件夹,下面的项目就是“点亮LED灯”的项目,所谓的“Hello,world!”就是他了。我们把它拷贝出来放到一个自己建立的干净的文件夹下,便于分析文件系统,我放在F:\ER\AT91SAM3S4C\01-codeExample下面。以下以此目录为例。
然后双击F:\ER\AT91SAM3S4C\01-codeExample\08-Led_Controller\project下的SAM3S4C.uvproj打开项目。
我们试图编译一下这个项目,看有什么现象,只有编译通过才说明我们没有遗漏文件,这也是为什么我把这个项目拷贝到一个干净的目录下的原因。编译之后如图1所示。
图1. 在MDK中编译官方的例程结果
我们发现编译没有成功,出错了,除了警告之外错误大部分是类似“..\..\common\peripherals\source\wm8731.c(101): error: #20: identifier "WM8731_REG_RESET" is undefined”这样的,意思是说找不见某某文件。(这里“..\”是上一级目录的意思)于是我们发现一个问题,在这个项目往上找两级目录,我们没有找到common这个文件夹,自然这个文件夹下的文件是找不到的,这说明我们遗漏有文件。
仔细观察一下官方的这个例子,我们发下它加入了好多文件,在MDK左侧的项目树at91lib节点下有很多adc、spi、pio……很多文件,从名字上我们也能看出,这是官方给每个模块写的库文件,类似STM32的“标准外设固件库”,找不见的文件都是这个目录下的,说明官方的库放在common文件夹下。
问题明确了,我们再次回到解压的那个目录下,果然发现,在这个08-Led_Controller文件夹同一级(也就是SAM3S4C.uvproj文件往上数两级----两个..\),找到一个common文件夹,我们把它照搬到F:\ER\AT91SAM3S4C\01-codeExample下。再次编译,发现编译成功!除了两个警告(warnings),这两个警告暂且不要管,起码项目是编译通过的。
说明我们完整的获得了一个参考例程,我们将这个例程下载到我们的开发板上,看看效果!(如果你使用的是H-Jtag不用改任何东西,只要打开H-Jtag Server即可。如果你使用的Jlink或者ULink,需要修改一下Debug调试器的类型。详细请参与第二课)。
然后我们点击Debug按钮,全速执行程序,发现板子上的蓝/绿两盏灯交替闪烁。我们完成了第一步,保证了参考程序的完整性,验证了官方程序的正确性。下一步我们开始分析这个例程。
二、知己知彼
1. 库文件就是----common 2. 官方的配置
MDK中项目配置是至关重要的,开始看看官方的例程的项目配置。打开项目配置“Option for target SAM3S4C Flash”(第二课说了在哪儿打开)。依次点上面的标签,我就不一个个点了,总结如下:(1)C宏定义:sam3s4
(2)C包含路径
..\..\common\peripherals;
..\inc;
..\project;
..\..\common\chip;
..\..\common;
..\..\common\usb\include;
..\..\common\chip\include
(3)Misc Control: --gnu
(4)Linker:
去掉Use Memory Layout from Target Dialog 前面的勾,增加Scatter file: ..\common\flash.sct
(5)Debug的初始化文件
..\..\common\sam3s-ek-flash.ini
3.项目组织结构 文件多了我们需要分组,尤其适用库函数的时候,我们点开“Components、Evironment and books”,查看分组情况,总结如下。
(1)startup--common\workaround.s
(2)at91lib--(官方在不同的项目中没有包涵完,这个可以理解,没有使用的外设我们没有必要全包涵进来,会白白增加编译时间)
common\peripherals\source
common\chip\source\
(3)usr--用户自己的程序(main.c、……)
(4)ReadMe--ReadMe.txt(关于项目的说明)
本小节解惑:
1. 有同学要说,为什么要扒这些东西?
不知道项目配置就不知道哪些文件是必要的,而且刚开始很多稀奇古怪的问题都是配置引起的,所以扒这个是为了后面少麻烦。
2. 我怎么知道上面这些配置是原来就有的,还是官方加的?
好办,自己建一个空的项目,然后一项项对比-_-#,刚开始不要嫌麻烦,坚持住!
三、照猫画虎(法宝在手,代码不愁)--第一个项目,点亮一盏LED灯
1. 按照官方的模样,建项目(01-ToggleIO)。 目录结构:
01-ToggleIO│
├─common
│ ├─chip
│ │ ├─cmsis
│ │ ├─include
│ │ └─source
│ ├─fat
│ ├─LIB
│ ├─memories
│ │ ├─build
│ │ │ └─mdk
│ │ ├─include
│ │ ├─nandflash
│ │ ├─norflash
│ │ ├─sdmmc
│ │ └─spi-flash
│ ├─peripherals
│ │ ├─include
│ │ └─source
│ ├─startup
│ └─usb
│ ├─common
│ │ ├─audio
│ │ ├─cdc
│ │ ├─core
│ │ └─hid
│ ├─device
│ │ ├─audio-speaker
│ │ ├─audio-speakerphone
│ │ ├─ccid
│ │ ├─cdc-serial
│ │ │ └─drv
│ │ ├─composite
│ │ │ └─drv
│ │ ├─core
│ │ ├─hid-keyboard
│ │ ├─hid-mouse
│ │ ├─hid-transfer
│ │ └─massstorage
│ └─include
├─inc
├─project
│ ├─flash
│ └─sram
├─ReadMe
└─src
注意:
(1)这里做了一点小小的修改,官方把commom放到项目文件夹外边了,然后采用文件包含的形式来引用,这样便于多个项目同时共享这个库,但是也有个不方便的地方,就是我们的库不独立。为了每个项目相对独立,这里我把他放到项目文件夹的下面。好处我们后面可以看到,这里为了不增加理解的难度,暂且不表,这么搞就行了。
(2)project下有两个文件夹flash和sram暂且不要管,反正刚开始也看不懂,以后再去研究。(可以大胆猜猜,留在后面我再说^^)
2. 拷贝官方的库文件common到工程目录下(上面已经做了这一步)
3. 在MDK中建立项目(略过),按照官方的配置,如上面的第二部分讲的那样,配置项目,这里引用库的时候请引用01-ToggleIO里面的这个库。
4. 打开官方的项目,开始照猫画虎。(该亮法宝了!---“Go To Difinition of 'xxx'”)
我们看官方的文件从哪儿开始看?第一行?往下看?No,No……这样就挂了!从main函数开始看
官方main函数我的分析如下,刚开始要大胆的猜,错了我们再撤销就行了:
extern int main( void )
{
/* 这两句好象是说对于Keil µVision 4做了点额外的工作,暂且保留 */
#if defined ( __CC_ARM ) /* Keil µVision 4 */
/* Disable semihosting */
# pragma import(__use_no_semihosting_swi)
#endif
/* Disable watchdog(关闭看门狗,大家都认识,保留!从这里我们获得到一个额外的信息-->AT91SAM3S上电默认是开着看门狗的) */
WDT_Disable( WDT ) ;
/* Output example information(从printf可以看出这个是想通过串口输出信息给用户的,我们做的是Hello,world!实验,就点个灯,为了给自己减小难度,删掉!)*/
printf( "-- Getting Started Example %s --\n\r", SOFTPACK_VERSION ) ;
printf( "-- %s\n\r", BOARD_NAME ) ;
printf( "-- Compiled: %s %s --\n\r", __DATE__, __TIME__ ) ;
/* Configure systick for 1 ms. (这个是在配置Systick定时器设置1ms延时的,得要!保留!一会儿我们的灯要闪烁。)*/
/* 这个配置有提示用户的容错功能,我们刚开始用不到,我暂且相信悠龙给的这个板子没有问题,是好的,刚才也测试了确实是好的,所以不用容错测试,我们将本句改一改。
while(SysTick_Config( BOARD_MCK / 1000 ) != 0);
这句不用我说了吧?都能看懂,就是你要是配不好,我就在这里死等。这里有两个东西可能让人晕,这个BOARD_MCK是个神马东西?
你怎么知道SysTick_Config函数返回的是0和非0呢?这个就要善于使用MDK的“Go To Difinition of 'xxx'”,在你不认识的常量、变量、函数等等上点右键,选择“Go To Difinition of 'xxx'”,你就能看到这东西到底是什么,可以一层层去追溯,你一定会弄明白的,这个精神很重要!这就是我们克敌制胜的法宝。这个任务就教给大家自己去看了,这里不表。这个过程如图2所示。
图2. 我们的“法宝”
*/printf( "Configure system tick to get 1ms tick period.\n\r" ) ; //这个就没必要了,删掉!
if ( SysTick_Config( BOARD_MCK / 1000 ) )
{
printf("-F- Systick configuration error\n\r" ) ;
}
/* PIO configuration for LEDs and Buttons. (初始化LED和Buttons,LED我们用的到,Buttons先算了,上法宝找到这个函数,在pio_it.c中,原来是配置中断,我们用不着,删掉!)*/
PIO_InitializeInterrupts( IRQ_PRIOR_PIO ) ;
/* 还是法宝,找到_ConfigureTc函数,看解释,原来是配置定时器0的,我们显然用不到,大大方方的删! */
printf( "Configure TC.\n\r" ); //根据上面描述,这一句铁定要删除的
_ConfigureTc() ;
/* 法宝真的很管用-_-# 找到_ConfigureLeds函数,这里有惊喜!看介绍“Configure LEDs”,这个是我们要用的,我们点过去看看。
static void _ConfigureLeds( void )
{
LED_Configure( 0 ) ; //发现这里又是一个不认识玩意儿,不怕!我们继续“Go To Difinition of 'LED_Configure'”
LED_Configure( 1 ) ;
}
在LED.c中我们找到了LED_Configure函数,如下:
extern uint32_t LED_Configure( uint32_t dwLed )
{
#ifdef PINS_LEDS
// Check that LED exists(一看就是容错机制,不用管,我们大方删掉!)
if ( dwLed >= numLeds)
{
return 0;
}
// Configure LED
return ( PIO_Configure( &pinsLeds[dwLed], 1 ) ) ; //只剩下这一句是硬货!我们有法宝,继续上绝招,找PIO_Configure函数
#else
return 0 ;
#endif
}
又扒了一层,在pio.c中我们找到了PIO_Configure函数,太长,不写了,请大家自己看,而且函数有解释,其实是配置一个IO口的函数。
想想也是,点灯不就是把一个IO口配置成推挽输出模式,然后一会儿给1,一会儿给0么?有点眉目了。
函数原型是uint8_t PIO_Configure( const Pin *list, uint32_t size )。
第1个参数是一个Pin类型的指针类型,有同学又蒙了,这个Pin又是啥玩意儿?别蒙,我们有法宝!上绝招我们找到了Pin的定义。
/*
* Describes the type and attribute of one PIO pin or a group of similar pins.
* The #type# field can have the following values:
* - PIO_PERIPH_A
* - PIO_PERIPH_B
* - PIO_OUTPUT_0
* - PIO_OUTPUT_1
* - PIO_INPUT
*
* The #attribute# field is a bitmask that can either be set to PIO_DEFAULt,
* or combine (using bitwise OR '|') any number of the following constants:
* - PIO_PULLUP
* - PIO_DEGLITCH
* - PIO_DEBOUNCE
* - PIO_OPENDRAIN
* - PIO_IT_LOW_LEVEL
* - PIO_IT_HIGH_LEVEL
* - PIO_IT_FALL_EDGE
* - PIO_IT_RISE_EDGE
*/
typedef struct _Pin
{
/* Bitmask indicating which pin(s) to configure. */
uint32_t mask;
/* Pointer to the PIO controller which has the pin(s). */
Pio *pio;
/* Peripheral ID of the PIO controller which has the pin(s). */
uint8_t id;
/* Pin type. */
uint8_t type;
/* Pin attribute. */
uint8_t attribute;
} Pin ;
看到这儿我都不想解释了。人家说的多详细啊!
可以看到type和attribute的值不用我们操心了按照上面的配置即可,Pio我们可以使用绝招找到。
但是有的同学又蒙了,mask/id又是什么?这里发现法宝不好使了。。。没法再继续了。。。别慌,有办法。我们有官方例程怕什么呢?
官方例程中PIO_Configure( &pinsLeds[dwLed], 1 )中我们找到pinsLeds看看又是什么呢?这个不就是Pin类型么?
上法宝我们找到了static const Pin pinsLeds[] = { PINS_LEDS } ;还没有到头,继续,我们又找到了#define PINS_LEDS PIN_LED_0, PIN_LED_1, PIN_LED_2
还没有到头,继续!
终于!妈的,藏的够深的啊,找到了这个#define PIN_LED_0 {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
我们对比一下,猜都能猜出这是PA19这个端口的配置,对照板子原理图,它是我们的D2,蓝灯。
没有白找,现在我们总结一下收获:
(1)Pin数据类型是 {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}这种结构
我们照猫画虎啊如果是PB10应该是什么样的呢?{PIO_PB10, PIOB, ID_PIOB, 默认输出, 配置参数},猜出来这样的。就对了!
(2)PIO_Configure( const Pin *list, uint32_t size )函数用于配置IO口,使用PIO_Configure((在代码中定义的Pin类型的数组首地址),这个数组中定义的IO个数)
Const类型说明指向的是Flash区域,而非Ram,说明是写在代码里面的。这里也解释了配置端口可以一次性配置好多,写成数组的形式。
size是指要初始化的IO口的个数,因为编译器是不知道你这个list到底有多长。这个个数要注意,可以少于这个数组中定义的个数,但是不能多,为什么呢?自己想。。。
如果我懒得算这个数,怎么办?官方有个小工具PIO_LISTSIZE(数组名),就能自动算出你指定的这个数组中定义了几个IO。详细自己找,算是课后作业吧。
(3)这里学会的是方法,不是为了找这几个小破函数,我们的绝招是我们学习的法宝!时刻记住,我们有法宝!
*/
printf( "Configure LED PIOs.\n\r" ) ; //根据上面描述,这一句铁定要删除的
_ConfigureLeds() ; //这个扒的最长,见上面,法宝已经亮出,下面的我不打算再分析了,作为作业,请大家自行分析*/
printf( "Configure buttons with debouncing.\n\r" ) ; //根据上面描述,这一句铁定要删除的
_ConfigureButtons() ; //我们不用buttons,删除! 建议大家有空看看,也是配置IO口,跟上面LED很像,但这个是配置输入的
printf( "Press USRBP1 to Start/Stop the blue LED D2 blinking.\n\r" ) ;//删除
printf( "Press USRBP2 to Start/Stop the green LED D3 blinking.\n\r" ) ;//删除
while ( 1 )
{
/* Wait for LED to be active (作业,请自己扒) */
while( !bLed0Active );
/* Toggle LED state if active */
if ( bLed0Active )
{
LED_Toggle( 0 ); //重点扒这一句,能够扒出一个PIO_Clear函数用于把一个IO口输出0,PIO_Set函数用于把一个IO置1
printf( "1 " );
}
/* Wait for 500ms */
_Wait(500); //作业,自己扒,法宝在手,代码不愁!
}
}
通过上面的分析,我写出下面的一个简单的程序---Hello, world!,供大家参考,所用的函数都是上面用法宝扒过的。新加的内容我有解释。
5. 新建main.c文件,把下面的内容粘贴过去:
/* 包含文件:官方这么包含的,我们就先这么包含,别总想着往里面看,先把现象搞出来,再去试着改 */
#include "board.h"
#include /* 等这个程序可以正常运行的时候,我们试着去掉stdbool和stdio头文件,看有没有影响,现在先留着,保持文件的完整性。程序运行后我们删掉这两个包含文件发现没有影响,那说明:可以删!不过这个过程希望大家自己尝试,不要光听我的 */
#include
/* D2(蓝灯)PA19,推挽输出模式 */
static const Pin LED_2 = {PIO_PA19, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT};
/*****************************************************************************
* Local functions
*/
/*----------------------------------------------------------------------------
* function name: SysTick_Handler
* parameter : void
* return : none
* brief : Handler for SysTick interrupt. Increments the timestamp counter.
*/
void SysTick_Handler( void )
{
TimeTick_Increment() ;
}
/*****************************************************************************
* Global main
* Application entry point for smc_lcd example.
* return Unused (ANSI-C compatibility).
*/
extern int main( void )
{
/* local variables */
uint8_t i=0;
/* Disable watchdog */
WDT_Disable( WDT ) ;
/* Configure systick for 1 ms. */
while(SysTick_Config( BOARD_MCK / 1000 )!= 0);
/* Configure & initialize the PIO */
PIO_Configure(&LED_2, 1);
/* Infinate circle */
while(1)
{
PIO_Set(&LED_2); //turn off D2
Wait(500); //waiting for 500 ms
PIO_Clear(&LED_2); //turn off D2
Wait(500); //waiting for 500 ms
}
}
6. 只要是按照官方的配置配的项目和项目分组,只修改着一个main.c文件,编译就能通过,下载到板子上发现D2蓝色的灯就会以500ms交替闪烁。
四、今天迈出一小步,是我们进入AT91SAM3S世界的一大步!
今天的小试,点亮了LED灯,我们的Hello,world!实验到此结束,这是我们踏进AT91SAM3S的第一步,要熟练项目的配置,至于这些配置为什么是这样的我先不说,如果有经验的同学肯定都猜出来一些,其实只要我们熟悉一种MCU,这些都是相通的,我暂且不说。
本节只要能对照着官方例程建项目、项目分组即可,不用死记硬背,这些熟练了自然就记住了。本节重点是会使用“法宝”探索新例程(这是重点!)只要这种精神存在,搞定官方案例只是时间的问题。
始终记住:模仿是创新的基石。不要一上来就想改很多东西,这样只能给自己增加难度,改出问题了只会打击自己的信心。知己知彼,才能百战百胜,先搞定官方的心思。
敲字真的很累,语言表达真的胜过文字,我上课的时候这些内容50分钟就能说完,敲字敲了2小时,还忽略了一些基本的操作,打算后面的课程录视频。如果对我的描述有不清楚的地方欢迎跟贴提出,或者QQ我。下一节我们来对这个案例进行丰富,增加按键的功能,下节继续!March on!
楼主你好,我是一个半路出家的搞嵌入式的学生,导师很N,开学就给我一个SAM3S4C板子让我自己回来先自己玩==可是我基础薄弱,以前也没好好学51,现在很捉急,每天都在研习代码,可是由于是半路出家遇到很多困难,不知道能否发个邮件和我取得联系方式,我的邮箱是mortoncui@foxmail.com,如果能给我一些指导,不甚感激。
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |