注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

深海精灵

水里的一滴油,透明地飘零在空气中。。。

 
 
 

日志

 
 
关于我

神秘诡谲,令人费解。可以很执着,也可以很破坏。冰冷,沉默。渴望单纯,渴望自由。躲在文字的背后,用心聆听世界,用直觉洞察世情。

网易考拉推荐

Nuvoton芯唐(新唐)32位Cortex-M0单片机快速上手快速入门篇  

2012-06-25 15:42:21|  分类: Cortex-M0 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

  本篇讲述的是快速入门,突出一个"快"字,力求以最快速度了解该款单片机的开发环境、基础设置等大概信息,具体功能不作详细描述,部分不清楚的模块暂时放下不管,如定时器、AD等多模式可选情况下,只随机选一种模式进行设置,不展开描述,目标是以最快的速度,让单片机跑起来。

  话不多说,马上进入正题。

  Nuvoton芯唐(新唐)32位Cortex-M0单片机在市面上已推出多年,价格比Cortex-M3要便宜,更接近51系列的单片机,性能当然是比51系列的强,又略逊于M3系列了。

 

一、首先,到Nuvoton官方网站www.nuvoton.com了解一下,上面有提供数据手册和开发资料。从选型表中选出符合项目需求的片子,再针对相应系列的芯片下载相关的资料。

  我这次选的是MO58LBN,也整理了一些相关资料http://dl.dbank.com/c0okfo2ypn

  由于芯片在出厂时不像STM32那样固化了ISP程序,所以要选购一个仿真器或烧录器,在ISP之前要先把ISP code灌进芯片中(这类似51系列)。芯唐的单片机提供的程序烧录方式有很多种,这里就说说其中一种,其余就不一一介绍了。

  我选用的是DzLink,是大中科技基于NuLink开发的,针对这个在网上找资料可与NuLink相同,下面也把它称为NuLink。

  芯片,板子(板子电路设计就不说了,按项目需求设计吧),仿真器等硬件都准备好了,就来讲讲开发环境吧。NuLink据说可支持IAR541~IAR620和Keil,安装相应的Driver就可以了。我一直在用的是IAR630,咨询过芯唐的人员,据说Driver_v18.5320也是支持的,但试了几次都没连上,为了这个“快”字,决定先放下,选用了Keil。Driver安装完之后可见驱动之NuLink用户手册,根据上面所说的步骤进行配置,步骤也是很简单的。

  既然要快,总不能裸奔吧,所以接下来看一下Library,官网提供了几个。M051SeriesBSP_CMSIS_v2.01.001.zip更接近M3系列的开发,有IAR的Project;而M051SeriesBSP_DirectRegisterAccess_SC_v1.01.001.zip更接近51系列的开发,直接控制寄存器。这次选用了M051SeriesBSP_DirectRegisterAccess_SC_v1.01.001(随机选的)。

 

二、该选的都选好了,就开始开发环境的建立了。

1、安装Keil和NuLink_Driver就不多说了。打开Keil,建立一个新Project,Database选NuMicro Cortex M0 Database,芯片选MO58LBN

Nuvoton芯唐(新唐)32位Cortex-M0单片机快速入门快速上手篇 - 深海精灵 - 深海精灵
 
Nuvoton芯唐(新唐)32位Cortex-M0单片机快速入门快速上手篇 - 深海精灵 - 深海精灵
 
Nuvoton芯唐(新唐)32位Cortex-M0单片机快速入门快速上手篇 - 深海精灵 - 深海精灵
 
Nuvoton芯唐(新唐)32位Cortex-M0单片机快速入门快速上手篇 - 深海精灵 - 深海精灵
 
 
2、项目配置完后向项目添加文件,还记得刚才提到的Library吗?官网提供的M051SeriesBSP_DirectRegisterAccess_SC_v1.01.001里面是有很多例程的,Library只需要里面Include文件夹中的MO51.h和Register_Bit.h两个文件,Keil建项时加入的startup_M051Series.s相当于Include中的startup_M051.s(如果在建项目没有要Keil自己加的startup就要自己添一个了),关于startup要注意的是,SystemInit入口要删掉(如下):
                EXPORT  Reset_Handler             [WEAK]
                ;IMPORT  SystemInit
                IMPORT  __main
    ……………………
                ;LDR     R0, =SystemInit
                ;BLX     R0
                LDR     R0, =__main
                BX      R0
                ENDP
 
 3、添加main.c
  我们不用传统的HelloWorld,而用更加简单快捷的:
#include <stdint.h>
main(void)
{
  uint16_t i;
  while(1) i++;
}
  编译——>仿真——>单步运行
  测试仿真器是否连接成功,变量i是否逐步增加
  这样,开发环境就建立起来了,但这时还没有让单片机跑起来,还要继续努力。
 
三、
1、寄存器的解锁保护
  芯片中的某些寄存器的访问是被保护的,防止由于系统干扰引起这些位被修改,从而引起系统崩溃。
void Un_Lock_Reg(void) 
{  // 解保护
    RegLockAddr = 0x59;
    RegLockAddr = 0x16;
    RegLockAddr = 0x88;
}
void Lock_Reg(void)
{  // 锁寄存器
    RegLockAddr = 0x00;
}
 
2、系统时钟源设置

static void RCC_Config(void)
{
  Un_Lock_Reg();  
  PWRCON |= OSC22M_EN;   // 内置的22.1184MHZ
    while((CLKSTATUS & OSC22M_STB) == 0);
  PLLCON = 0x0008D25E;
    while((CLKSTATUS & PLL_STB) == 0);
    CLKSEL0 = (CLKSEL0 & (~HCLK)) | HCLK_PLL;
  Lock_Reg();
}

 

3、最基础的I/O口设置:根据板上需要,把IO设置为输入/输出/开漏/复用功能等

static void GPIO_Config(void)
{
  P0_PMD = 0x0000;
  P1_PMD = 0x0000;
  P2_PMD = (Px0_OUT | Px1_QB | Px2_OUT | Px3_OUT | Px4_OUT | Px5_IN | Px6_IN | Px7_IN);
  P3_PMD = (Px2_QB);
  P4_PMD = 0x0545;
}

  I/O口的控制直接向Px_DOUT写数即可,如:
  P4_DOUT = P4_DOUT^(1>>3);  // P4.3取反

4、为了方便测试,先把串口配置好,以便测试时输出需要的数据
  串口模式2的波特率 = UART_CLK / (UA_BAUD + 2)

/*--------------------------------------------------------------------------------------*/
void UART0_Config(void)
{
    P3_MFP = P3_MFP & (~(P31_TXD0 | P30_RXD0)) | (TXD0 | RXD0); //配置P3.1和P3.0为UART功能

    IPRSTC2 |= UART0_RST;                            //复位UART模块
    IPRSTC2 &= ~UART0_RST;                           //UART模块从复位状态恢复到正常工作状态

    APBCLK |= UART0_CLKEN;                           //使能UART时钟
    CLKSEL1 = CLKSEL1 & (~UART_CLK) | UART_22M;      //选择内部22M作为UART时钟源
    CLKDIV &= ~(15<<8);                              //设置UART时钟除频值为0

    UA0_FCR |= TX_RST;                               //复位发送FIFO
    UA0_FCR |= RX_RST;                               //复位接收FIFO

    UA0_LCR &= ~PBE;                                 //校验位禁止
    UA0_LCR = (UA0_LCR & (~WLS)) | WL_8BIT;          //数据宽度为8位
    UA0_LCR &= NSB_ONE;                              //1位停止位

    UA0_BAUD |= DIV_X_EN;                            //模式2:DIV_X_EN = 1
    UA0_BAUD |= DIV_X_ONE;                           //模式2:DIV_X_ONE =1
   
    UA0_BAUD |= ((22118400 / 115200) - 2);           //设置波特率为115200
}
/*--------------------------------------------------------------------------------------*/
void Send_data(uint8_t dat)    //通过UART发送一个字符
{
    while((UA0_FSR&TX_FULL) != 0);   
    UA0_THR = (uint8_t) dat;                          
}
/*--------------------------------------------------------------------------------------*/
uint8_t Receive_data(void)                      //返回接收到的字符
{
    while((UA0_FSR&RX_EMPTY) != 0);
    return ((uint8_t)UA0_RBR); 
}
/*--------------------------------------------------------------------------------------*/
main(void)
{
  BSP();    // 底层设置(包括RCC_Config()、GPIO_Config()、UART0_Config())
  while(1)
  {
    if(Receive_data()==0xD1)     // 测试串口接收和发送数据功能
    Send_data(0xD1);
  }
}

5、定时器设置
  定时时间 = CLKIN * (8位分频TCSR0 + 1) * TCMPR
void Timer0_Config(void)  // 定时2ms
{
    IPRSTC2 |= TMR0_RST;                                   //这两句是复位Timer0外设
    IPRSTC2 &= ~TMR0_RST;                                  //使能Timer0时钟源
    APBCLK |= TMR0_CLKEN;                                  //使能Tmier0时钟源
    CLKSEL1 = (CLKSEL1 & (~TM0_CLK)) | TM0_22M;            //选择22M做为Timer0时钟源
 
  TCSR0 = TCSR0 & (~TMR_MODE) | MODE_PERIOD;     //周期模式
  TCSR0 = TCSR0 & 0xFFFFFF00;                    //不分频
  TCMPR0 = 44000;                                // 中断周期 = CLKIN * (8位分频+1) * TCMPR = (1/22M) * 44000 = 2ms
    TCSR0 |= TMR_IE;                                       //使能中断
    NVIC_ISER |= TMR0_INT;                                  //使能中断向量
    TCSR0 |= CRST;                                         //复位Timer0
    TCSR0 |= CEN;                                          //使能Timer0
}
/*-----------------------------------------------------------------------------*/
void TMR0_IRQHandler(void)
{
  static uint8_t i=0; 
  if((i++)>100)
  {
    i = 0;
   P4_DOUT = P4_DOUT^(1>>4);   // 200ms取反一次(用示波器观察输出信号)
  }
    TISR0 |= TMR_TIF;                                      //清中断标志
}

 

6、PWM设置
  PWM周期 = PWMCLK * 预分频值(PPRA) * 分频值(CSRA) * CNR
  CNR递减计数
  CMR占空比说明:低电平 = CNR - CMR  高电平 = CMR
/*--------------------------------------------------------------------------------------*/
void PWM_Config(void)
{
  P2_MFP &= ~P22_AD10_PWM2;
  P2_MFP |= PWM2;
  APBCLK  |= (PWM23_CLKEN);
    CLKSEL1  = CLKSEL1 & (~(PWM23_CLK));
    CLKSEL1 |= (PWM23_22M); 
    PPRA  = 0x00003F00;     // 设置预分频值和死区长度 
    CSRA  = CSRA & (~CSR2);
    CSRA |= (CSR2_CLK_1);    //设置分频值
    PCRA |= (CH2_AU_RL);   //设定为自动重载模式
    CNR2A = 0x565;                    //设定PWM2周期
    CMR2A = 0x200;                    //设定PWM2高占空比  
    POEA |= (PWM2_OE);          //输出使能
    PCRA |= (CH2EN);                 //使能PWM计数器开始计数
}
  用示波器观察P22输出的信号,调节CMR和CNR的数值并观察变化

 

7、中断设置
  (将P32设为EXINT0外部中断,P00为GPIO中断
/*--------------------------------------------------------------------------------------*/
void EXIT_Config(void)
{
  P0_MFP = P0_MFP | GP01_INT;// | P00_SCHMITT;
  P0_PMD    = P0_PMD & (~Px0_PMD) | Px0_IN;
    P3_MFP    = (P3_MFP & (~P32_EINT0)) | EINT0;   //设定P3.2为外部中断0功能
    IPRSTC2  |= GPIO_RST;                            //复位GPIO外设
    IPRSTC2  &= ~GPIO_RST;                          //GPIO模块从复位状态恢复到正常工作状态
    P3_PMD    = P3_PMD & (~Px2_PMD) | Px2_QB;      //配置P3.2为准双向模式
 
    DBNCECON &= ~ICLK_ON;                          //如果所有IO口均没有使能中断,则禁止中断发生电路时钟
    DBNCECON &= DBCLK_HCLK;                        //去抖计数器时钟源为HCLK
    DBNCECON |= SMP_16CK;                         //每16个周期采样一次中断输入
    P3_DBEN  |= DBEN2;                             //使能P3.2引脚去抖功能

    P3_IMD   &= IMD2_EDG;                          //配置下降沿触发中断
    P3_IEN   |= IF_EN2;                            //使能P3.2引脚低电平或下降沿中断
    NVIC_ISER |= EINT0;                             //使能NVIC外部中断0中断
 
 DBNCECON |= ICLK_ON;
  P0_DBEN  |= DBEN0;
    P0_IMD   &= IMD0_EDG;                          //配置下降沿触发中断
    P0_IEN   |= IF_EN0;                            //使能P3.2引脚低电平或下降沿中断
  NVIC_ISER |= GP01_INT;                             //使能NVIC的GP01_INT中断
}
(这里有两个中断函数作为回应)
/*-----------------------------------------------------------------------------*/
void EINT0_IRQHandler(void)
{
  static uint8_t u = 0;
  Send_data(0xBB);         // 送出测试数据以证明中断产生
    P3_ISRC = P3_ISRC;                             //清P3中断标志
}
/*-----------------------------------------------------------------------------*/
void GPIOP0P1_IRQHandler(void)
{
  if((P0_ISRC&0x01)!=0)
  {
    Send_data(0xCC);         // 送出测试数据以证明中断产生
    P0_ISRC = P0_ISRC;                             //清P01中断标志
  }
}

 

8、ADC设置
  ADC时钟频率 = ( ADC时钟源频率 ) / (ADC_N+1)
/*--------------------------------------------------------------------------------------*/
void ADC_Config(void)
{
    IPRSTC2 |= ADC_RST;                                    //复位ADC外设
    IPRSTC2 &= ~ADC_RST;                                   //ADC模块从复位状态恢复到正常工作状态
    APBCLK  |= ADC_CLKEN;                                  //使能ADC时钟
    CLKSEL1  = CLKSEL1 & (~ADC_CLK) | ADC_22M;             //选择內部晶振作为ADC时钟源
    CLKDIV  |= 0x00050000;                                 //ADC时钟6分频
    ADCR    |= ADEN;                                       //使能ADC
    ADCALR |= CALEN;                                       //使能ADC自校验功能
    while(!(ADCALR & CALDONE));                            //等待校验完成
 
  ADCR     = ADCR & (~ADMD) | MD_SIG_SCN;        //设定AD转换器为单周期模式             
  ADCR    &= ~DIFFEN;                        //设定AD模拟电压输入模式为单端输入
  ADCHER  |= CHEN0 | CHEN1;                          //使能模拟输入通道0
  P1_MFP   = P1_MFP & (~(P10_AIN0_T2|P11_AIN1_T3)) | AIN0 | AIN1; //设置P1.0为ADC0模拟输入通道
  P1_OFFD |= OFFD0 | OFFD1;                          //禁止P1.0数字输入通道
  P1_PMD   = P1_PMD & (~(Px0_PMD|Px1_PMD)) | Px0_IN | Px1_IN;   //配置P1.0为输入功能
  ADSR    |= ADF;                            //开始转换前先清除ADC中断标志,防止中断意外触发  

  ADCR    |= ADIE;                           //使能ADC中断
  NVIC_ISER = ADC_INT;                       //使能NVIC里ADC中断向量 
}
/*-----------------------------------------------------------------------------*/
void ADC_IRQHandler(void)
{
    uint16_t uiADC1Value; 
  uiADC1Value = ADDR0 & 0x00000FFF;            //读取AD转换结果
  Send_data(uiADC1Value>>8);        //把P10数据送出来以检查其是否正确
  Send_data(uiADC1Value&0xFF);
  uiADC1Value = ADDR1 & 0x00000FFF;            //读取AD转换结果
  Send_data(uiADC1Value>>8);       //把P11数据送出来以检查其是否正确
  Send_data(uiADC1Value&0xFF);
    ADSR |= ADF;                                 //清除ADC中断标志  
}

 

9、SPI设置
  SPIclk = Pclk/((HCLK_N+1)*2)
该次SPI测试用的是AT93C46,用P0.4~P0.7的SPI1,设为主模式,CS高电平有效,CLK常低。
void SPI1_Master_Init(void)
{         // 设置为主模式初始化                     
    P0_MFP  &= ~(P04_AD4_SPI1SS  |
                P05_AD5_SPI1MOSI |
                P06_AD6_SPI1MISO |
                P07_AD7_SPI1CLK);   
    P0_MFP  |= (SPI1SS | SPI1MOSI | SPI1MISO | SPI1CLK);    //配置P0.4~7为SPI功能

    IPRSTC2 |= SPI1_RST;                                    //复位SPI
    IPRSTC2 &= ~SPI1_RST; 
    APBCLK  |= SPI1_CLKEN;                                  //使能SPI时钟
    CLKDIV  &= 0xFFFFFFF0;                                  //HCLK分频
    CLKDIV  |= 0x08;
   
    SPI1_CNTRL   &= ~SPI_MODE_SLAVE;                       //SPI主模式
    SPI1_CNTRL   &= ~LSB_FIRST;                             //高位在前
    SPI1_CNTRL   &= ~CLKP_IDLE_H;                           //CLK常低
    SPI1_CNTRL   |= TX_NEG_F;                               //SDO下沿改变
    SPI1_CNTRL   &= ~RX_NEG_F;                              //SDI上沿锁存
    SPI1_DIVIDER &= 0xFFFF0000;                             //SPIclk = Pclk/((HCLK_N+1)*2)
    SPI1_DIVIDER |= 0x00000600;                     //设置SPI的分频值 (该处分频值没有经过计算,只是实测满足条件)
 
    SPI1_SSR     |= LVL_H;                                 //SS_LVL = 1   CS高电平有效
    SPI1_SSR     &= ~ASS_AUTO;                              //不用自动从选择
}
/*---------------------------------------------------------------------------------*/
void Shift46_SPI(uint32_t ulData, uint8_t ucLength)
{  // 向93C46发送数据 
    SPI1_CNTRL &= TX_NUM_ONE;                               //一个收发
    SPI1_Length(ucLength);
    SPI1_TX0 = ulData;                                      //待发数据
    SPI1_CNTRL |= GO_BUSY;                                  //启动发送
}
/*---------------------------------------------------------------------------------*/
void SPI1_Length(uint8_t ucLength)
{ // 设置发送长度
    if(ucLength <= 0x20)
    {
        if((ucLength & 0x01) == 0)
            SPI1_CNTRL &= ~(1<<3);
        else
            SPI1_CNTRL |= (1<<3);

        if((ucLength & 0x02) == 0)
            SPI1_CNTRL &= ~(1<<4);
        else
            SPI1_CNTRL |= (1<<4);

        if((ucLength & 0x04) == 0)
            SPI1_CNTRL &= ~(1<<5);
        else
            SPI1_CNTRL |= (1<<5);

        if((ucLength & 0x08) == 0)
            SPI1_CNTRL &= ~(1<<6);
        else
            SPI1_CNTRL |= (1<<6);

        if((ucLength & 0x10) == 0)
            SPI1_CNTRL &= ~(1<<7);
        else
            SPI1_CNTRL |= (1<<7);
    }
}

  至此,M0单片机的大部模块已经可以正常跑起来,根据项目需要开发自己的APP吧。当然,不能就此满足,模块的具体设置还是要深入研究的,寻址空间内存地址、Flash存储控制等还需要不断的加强理解。

  最后,附上该次快速入门的例程http://dl.dbank.com/c0wau8sd0b


  评论这张
 
阅读(7867)| 评论(4)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017