关于 Nations N32L406 通过中断实现串口收发

一、 概述

        世平 BMS 方案主要用到国民技术的 N32L406 这款 MCU 来完成,本篇文章旨在制作过程中对于采用中断方式实现串口收发处理的一些分析。下面介绍一下 N32L406 这颗芯片。

        国民技术 N32L406 ,采用高性能 32 位 ARM Cortex-M4 内核 ,最高工作主频 64 MHz,支持浮点运算和 DSP 指令,集成高达 128KB 嵌入式加密 Flash,最大32KB SRAM、集成丰富的高性能模拟器件,内置 1 个 12 bit 4.5 Msps ADC,内置一个内部高速AHB总线, 两个低速外设时钟总线APB及总线矩阵,1个1Msps 12bit DAC, 5个U(S)ART、 1个LPUART、 2个I2C、 2个SPI/ I2S、 1个全速USB 2.0设备、 1个CAN 2.0B通信接口,内置密码算法硬件加速引擎。框图如图 1 所示。

                                                                                                       图 1 N32L40x 系列框图

        此次实现中断模式串口收发主要参考了国民技术 Nationstech.N32L40x_Library.1.0.0 版本 SDK 里的固件库和源代码。SDK 支持 KEIL5 平台和 IAR-EWARM 平台。我们使用的是 KEIL5 编译。本文大致分为三个部分,第一部分:概述:介绍方案所采用的国民 N32L40x 系列的芯片以及一些参数和文章的大概架构;第二部分:国民技术 SDK 介绍:介绍了国民 SDK 内的一些文件夹包含的主要内容以及相关功能;第三部分:KEIL5环境搭建:讲述如何使用 KEIL5 搭建编译环境进行相关设置;第三部分:复制例程:第一小部分先查看一下例程, examples → USART → Interrupt 打开 MDK ,找到串口中断收发函数,并对其进行理解,第二小部分再说明如何从 SDK 移植到自己的工程中。

二、 国民技术 SDK 介绍

 

                                   图 2  Nationstech.N32L40x_Library.1.0.0

        如图 2 ,在 Nationstech.N32L40x_Library.1.0.0 文件夹里,包含 Firmare 文件夹和 Project 文件夹。Nationstech.N32L40x_Library.1.0.0 文件夹下包含了固件库的源代码和启动文件,是固件库的核心所在。如果自己创建工程的话,需要包含里面的大部分源代码。

        1. Firmare 文件:固化的软件,固化在集成电路内部的程序代码。其中最主要的就是 CMSIS 文件夹和 n32l40x_std_periph_driver 文件夹。

              1.1 CMSIS文件:( Cortex Microcontroller Software Interface Standard ) ARM Cortex™ 微控制器软件接口标准,各芯片厂商就得按照这个标准去编写自己芯片内核的驱动程序,比如系统函数的命名、芯片初始化启动流程等。用于存放符合CMSIS标准的文件。

                    1.1.1 Core文件:core 是内存的意思,其实就是内存的映像,当程序崩溃时,存储内存的相应信息,主要用于对程序进行调试。当程序崩溃时便会产生core文件。core_cm4.h 包含的是一些内核相关的函数和宏定义,例如核内寄存器定义、部分核内外设的地址等等,都是很底层

                    1.1.2 Device文件夹:存放一些必要的文件例如启动文件、系统文件等。主要包含如下几个部分。

                            a、Startup_n32l40x.s——设备启动代码,包括复位处理程序和异常向量。是每一个程序都必须添加的代码。这个 Starup 文件里包含了Startup 的入口函数,当系统上电或复位时,这是第一个被执行的函数。这个函数都是由汇编语言编写的,完成基于硬件平台的最初的初始化,这个函数执行到最后会跳转到 C 语言的入口,一般是 Main 函数。

                             b、System_n32l40x.c——设备的基本配置文件。在跳转到 Main 之前调用。包括初始化时钟的频率、系统时钟,设置中断向量表的基地址和偏移地址。

                             c、n32l40x.h ——用户代码需要的包含文件,用于访问设备。这是最重要的文件。定义各种中断向量表的顺序、数据类型、外设寄存器的定义、MCU 外设相关的结构体。

                             d、n32l40x_conf.h ——外设头文件的管家,include了大部分外设驱动头文件。在上层调用时只需要打开这个文件的注释即可。在实际使用中,会根据需要选择注释掉不需要用到的文件。

              2.1 n32l40x_std_periph_driver 文件夹:只包含了 inc 和 src 文件。是所有外设的头文件和源文件的集合,根据使用需求来添加。

                    2.1.1 inc文件: 外设头文件的集合,需要添加到路径。

                    2.1.2 src文件: 外设源文件的集合,需要添加到工程。 


              如图 3 所示,是 n32l40x_std_periph_driver 文件夹内的内容。

                                 图 3 n32l40x_std_periph_driver 文件夹打开后

        2. Project 文件:用于存放所建立的 .uvproj 的工程文件及其它文件。是一些官方写好的例程,例如图 所示。点进想要使用的例程之后进入到 MDK-ARM 文件夹即可打开 keil 例程。MDK-ARM 文件包含了 bin、listing、object 文件:
                    b. bin:binary(二进制)的简写,bin中存放着本工程编译生成的结果文件,可理解为可执行文件(.exe),是默认情况下的输出文件目录,也就是你的工程编译的结果
                    c. listing:Listings 和 Objects文件夹是 MDK 自动生成的文件夹,用于存放编译过程产生的中间文件。
                    d. object:翻译成对象,object中存放着分块编译的中间文件,之所以是分块的,推断是用于加快下次的编译,只修改有变动的块。OBJ是编译时的中间目录,也就是说在BIN中的内容出现之前,编译器是在这里进行一些文件创建,修改等工作的。等全部完成之后才复制到BIN目录中的。

              ①. src 文件夹存放 N32L40x_it.c 和 Main.c ,专门用来存放中断函数的c文件 和主函数。中断服务函数你可以随意放在其他的地方,并不是一定要放在N32L40x_it.c 。

              ②. inc 文件夹存放 N32L40x_it.c 和 Main.h ,头文件的配置文件。

 

三、 KEIL5环境搭建

3.1 文件夹建立

        先建立一个用于存放工程的文件夹,再在 Keil 中新建一个工程,添加新建 main.c 文件。

        下面要将官方的固件库包里的源码文件复制到我们的工程目录文件夹下面。打开官方固件库包,定位到我们之前准备好的固件库包的目录下面,将目录下面的 src,inc 文件夹 copy 到我们刚才建立的文件夹 project 下面。src 存放的是固件库的.c 文件,inc 存放的是对应的.h 文件。还有把 Nations 固件库里的 Firmare 文件夹直接复制到工程目录下

        Project / src 下面,将里面需要的文件选中,然后点击 Add,然后 Close.可以看到 Files 列表下面包含我们添加的文件,对于我们写代码,如果我们只用到了其中的某个外设,我们就可以不用添加没有用到的外设的库文件。如果全部添加进来工程太大,编译起来速度慢。如图 4,是新建工程后自动创建的文件夹。


                                           图 4 新建的文件夹

3.2  Manage Project Items 设置

双击图 2 中的 Nationstech.N32L40x_DFP.0.9.0.pack 文件,把芯片添加到 keil 中,在 keil 里新建一个工程,点击 Manage Project Items ,按照图 5 所示建好以下四个 group 。

打开查看 Nations 的 SDK (Nationstech.N32L40x_Library.1.0.0)里面包含需要添加的文件。

然后设置组的时候,按照如下的路径找到相应的文件添加到组里。

①加入 firmware → CMSIS → device → starup → startup_n32l40x.s 文件到STARTUP 组里。

②加入 firmware → CMSIS → device → system_n32l 40x.c 文件到 CMSIS 组里。

③加入firmare → n32l40x_std_periph_driver → src → n32l40x_gpio.c 、n32l40x_rcc.c、n32l40x_usart.c、misc.c 文件到 FWLB 组里。(此组只选择用到的外设,根据需求添加)

④加入 main.c(创建或添加例程)、projects → n32l40x_EVAL → examples →USART →Interrupt → src → n32l40x_it.c 文件到 USER 组里。


                                                                图 5  Manage Project Items 设置

 

3.3  Options for Target ‘ Target 1 ’ 设置

①选择芯片:如图 6 所示。

                                                                                        图 6 选择芯片

②C/C++ 中加入路径:如图 7 所示,把 .h 文件的路径添加进来。(将文件夹中含头文件代码的目录都添加进来)keil 只会在一级目录查找,所以如果你的目录下面还有子目录,记得 path一定要定位到最后一级子目录

                                                                                           图 7 添加路径

 

③选择对应的 debug ,如图 8 所示。

                                                                                         图 8 选择 debug

④这样一个工程模版建立完毕。下面还需要配置,让编译之后能够生成 hex 文件。同样点击魔术棒,进入配置菜单,选择 Output。然后勾上下三个选项。 其中 Create HEX file 是编译生成 hex 文件,Browser Information 是可以查看变量和函数定义。如图 9 所示勾选。



                                                                                 图 9 output 设置

四、复制例程

4.1 查看例程

        打开查看 Nations 的 SDK (Nationstech.N32L40x_Library.1.0.0)里面包含需要添加的文件和所需例程。例程存放在 project 文件中,找到 USART → Interrupt ,打开例程。这里说明了中断控制USART 的用法。如图 10 所示。



                                                                                                                                       图 10  Starup_n32l40x.s 文件中 USART 的中断

        Starup_n32l40x.s 文件中可以看到这些中断函数,找到我们需要的 USART ,对应数据手册来看我们用的是 USART1,那么对应的中断处理函数就是 USART1_IRQHandler。进入到对应的中断函数为下图 11 所示。

 

                                                          图 11  USART1_IRQHandler函数

此中断函数存放在 n32l40x_it.c 文件中。

 if (USART_GetIntStatus(USARTy, USART_INT_RXDNE) != RESET)

    {

        RxBuffer1[RxCounter1++] = USART_ReceiveData(USARTy);

        if (RxCounter1 == NbrOfDataToRead1)

        {

            USART_ConfigInt(USARTy, USART_INT_RXDNE, DISABLE);

        }

    }

首先判断 RXDNE 寄存器的状态,判断是否为 0 ,如果不为 0 ,执行 if 语句中的语句。

RXDNE 寄存器的定义如下:

读数据缓冲区非空中断和过载错误中断使能(RXDNE interrupt enable)。

如果该位置 1, USART_STS 寄存器中 RXDNE 或 OREF 被置位时产生中断。

0:读数据缓冲区非空中断和过载错误中断禁用。

1:读数据缓冲区非空中断和过载错误中断使能。

此断语句意思是:

        从接收数据寄存器中读取一个字节:因为当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来。 此时用 RxBuffer1 来读取寄存器中的数据。

判断接收到的数据的长度是否等于TX 发送的数据的长度。若相等,关闭 RXDNE 。因为:NbrOfDataToRead = RxBufferSize1; RxBufferSize1=TxBufferSize2。

if (USART_GetIntStatus(USARTy, USART_INT_TXDE) != RESET)

{

        USART_SendData(USARTy, TxBuffer1[TxCounter1++]);

        if (TxCounter1 == NbrOfDataToTransfer1)

        {

            USART_ConfigInt(USARTy, USART_INT_TXDE, DISABLE);

        }

    }

}

判断 TXDE 寄存器的状态。

TXDE 寄存器的定义如下:

发送缓冲区空中断使能(TXDE interrupt enable)。

如果该位置 1, USART_STS 寄存器中 TXDE 被置位时产生中断。

0:发送缓冲区空中断禁止。

1:发送缓冲区空中断使能。             

此段语句的意思是:将一个字节写入传输数据寄存器,因为代表发送缓冲区为空,可以往数据寄存器写入数据了。

 

        在 Main 函数里定义了uint8_t TxBuffer1[] = "USART Interrupt Example: USARTy -> USARTz using Interrupt"。编译下载一下,打开串口调试助手。可以看到串口调试助手中输出了这串文字。代码是可用的,如图 12 所示。

                                                                                      图 12 串口接收数据

 

4.2 复制例程

由于之前已经把 n32l40x_it.c 文件加到 USER 组里了,所以此时只需要把 main.c 中需要的部分复制过来。包括一些定义和 main 函数里的部分。

#define TxBufferSize1 (countof(TxBuffer1) - 1)

#define TxBufferSize2 (countof(TxBuffer2) - 1)

#define RxBufferSize1 TxBufferSize2

#define RxBufferSize2 TxBufferSize1

#define countof(a) (sizeof(a) / sizeof(*(a)))

uint8_t aRxBuffer;        //接收中断缓冲

uint8_t TxBuffer1[] = "USART Interrupt Example: USARTy -> USARTz using Interrupt";

uint8_t TxBuffer2[] = "USART Interrupt Example: USARTz -> USARTy using Interrupt";

uint8_t RxBuffer1[RxBufferSize1];

uint8_t RxBuffer2[RxBufferSize2];

__IO uint8_t TxCounter1 = 0x00;

__IO uint8_t TxCounter2 = 0x00;

__IO uint8_t RxCounter1 = 0x00;

__IO uint8_t RxCounter2 = 0x00;

uint8_t NbrOfDataToTransfer1 = TxBufferSize1;

uint8_t NbrOfDataToTransfer2 = TxBufferSize2;

uint8_t NbrOfDataToRead1 = RxBufferSize1;

uint8_t NbrOfDataToRead2 = RxBufferSize2;

__IO TestStatus TransferStatus1 = FAILED;

__IO TestStatus TransferStatus2 = FAILED;

extern uint8_t Uart1_Rx_Cnt;       //接收缓冲计数

 

定义了一些数据发送或者接收时需要存放的数组,并且可以通过判断 BufferSize 或者 Counter 来确认数据是否传输完成。TxBuffer1 这个数字里输入了一串文字:USART Interrupt Example: USARTy -> USARTz using Interrupt,即是在串口调试助手中成功打印出来的一串。

USART 的配置

int main(void)

{

RCC_Configuration();

  NVIC_Configuration();

  GPIO_Configuration();

  USART_InitType USART_InitStructure;

  USART_StructInit(&USART_InitStructure);

  USART_InitStructure.BaudRate = 115200;

  USART_InitStructure.WordLength = USART_WL_8B;

  USART_InitStructure.StopBits = USART_STPB_1;

  USART_InitStructure.Parity = USART_PE_NO;

  USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;

  USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;

  USART_Init(USARTy, &USART_InitStructure);// 配置USARTy

  USART_ConfigInt(USARTy, USART_INT_RXDNE, ENABLE);// 启用USARTy接收和发送中断

  USART_ConfigInt(USARTy, USART_INT_TXDE, ENABLE);// 启用USARTy接收和发送中断

  USART_Enable(USARTy, ENABLE);//使能 USARTy

  while (1)

    {

    }

这里对 USART 进行了一个配置,打开对应时钟,配置主要是波特率、数据长度、停止位、控制流、奇偶校验、还有 USART 的模式配置为可接收也可发送。

Main.C 文件中还存在如下几个函数的定义:

void RCC_Configuration(void)

{

    RCC_EnableAPB2PeriphClk(USARTy_GPIO_CLK , ENABLE);//使能 USART GPIO 引脚时钟

    USARTy_APBxClkCmd(USARTy_CLK, ENABLE);//使能 USART 时钟

}

RCC_Configuration 配置了 GPIO 和 USART 的时钟使能。此时要注意自己板子串口所对应的时钟是哪一个。

void NVIC_Configuration(void)

{

NVIC_InitType NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);// 配置优先级为0

NVIC_InitStructure.NVIC_IRQChannel = USARTy_IRQn;// 用来设置中断源

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能

NVIC_Init(&NVIC_InitStructure);

}

NVIC_Configuration,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。不同的中断中断源不一样。抢占优先级和子优先级的属性编号越小,表明它的优先级别越高。

void GPIO_Configuration(void)

{

    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStruct(&GPIO_InitStructure);// 初始化GPIO_InitStructure

    GPIO_InitStructure.Pi = USARTy_TxPin; //将USARTy Tx配置为复用推挽

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStructure.GPIO_Alternate = USARTy_Tx_GPIO_AF;

    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);

 

    GPIO_InitStructure.Pin = USARTy_RxPin;

    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;

    GPIO_InitStructure.GPIO_Alternate = USARTy_Rx_GPIO_AF;

    GPIO_InitPeripheral(USARTy_GPIO, &GPIO_InitStructure);   

}

       如果是output,那个一般选择no pull,这样,引脚才能根据你的output数据,进行正确输出。如果是input,那么需要看具体应用的默认输入值是0还是1.  如果默认是输入0,则最好配置为pull down,反之则配置为pull up。此时要注意自己板子串口对应的 RX & TX 的引脚是哪一个引脚,引脚配好。

        发现复制过去 int main() 里面内容之后,以上几个函数是没有定义的,所以找到原来例程的 main.c 文件,把这几个函数添加进去。RCC_Configuration() 开启了 USART 的时钟, NVIC_Configuration() 设置了 USART 的优先级,GPIO_Configuration() 设置了引脚复用为 USART ,编译成功。

 

五、总结

        例程 MDK 里面举例了如何使用一些外设和通讯以及具体配置哪些内容,对于我们使用是非常方便和快捷的。找到需要的内容复制过来然后把配置改成需要的样子。去了解固件库,使用它的库函数,尽量去用固件库常见的一些外设库,中断库等等。而不是避开固件库自己写代码——代码成百上千个,写起来是很费时费力很困难的。使用现成的稳定的有固定格式的代码,是比较好的。

        本文主要讲述了 SDK 里的文件内容和怎么样从国民技术的 SDK 里移植通过中断实现串口收发的一些相关函数和代码。并对发生中断服务函数和相关例如时钟和 GPO 和优先级的配置进行了一些理解,包括对于串口的配置要特别注意当复制例程过来的时候需要结合自己板子上串口对应的 IO 引脚和时钟,适当修改。

 

六、参考文档

[1] 《N32L40x 系列数据手册》

[2]  Nationstech.N32L40x_Library.1.0.0 固件库

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论