这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » AT91SAM3S微控制器及uTenux初步探索心路历程

共25条 3/3 1 2 3 跳转至
菜鸟
2013-07-27 01:44:17     打赏
21楼
这个很不错啊,相当给力,我当时摸索了好久都没搞定,还是楼主强悍

菜鸟
2013-07-30 02:28:27     打赏
22楼
第三课:Hello, World!(AT91SAM3S微控制器IO口编程初探)


写在前面:


  上周出差,一走就是7/8天,出差没有带板子,也就没有更新进程贴,今天回来就迫不及待的想更新帖子了。

  从最早接触的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!

高工
2013-08-27 15:47:40     打赏
23楼
LZ接着把未完成的进程补上吧……最后成为精华帖和大家分享!

菜鸟
2013-09-03 18:05:22     打赏
24楼
好好!最近忙项目,没上论坛,也没有更新帖子,我将继续!谢谢~~

菜鸟
2013-09-05 16:37:16     打赏
25楼

楼主你好,我是一个半路出家的搞嵌入式的学生,导师很N,开学就给我一个SAM3S4C板子让我自己回来先自己玩==可是我基础薄弱,以前也没好好学51,现在很捉急,每天都在研习代码,可是由于是半路出家遇到很多困难,不知道能否发个邮件和我取得联系方式,我的邮箱是mortoncui@foxmail.com,如果能给我一些指导,不甚感激。

 

 

 


共25条 3/3 1 2 3 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]