BLE开发笔记——串口的使用

进行软件开发,串口打印是个有效的调试方式,特别适用于没有显示设备的开发平台,所以为了方便调试必须要学会串口的配置和使用,在后期实现串口数据蓝牙转发的时候,也必须清楚串口的使用。本文就针对我自己的情况说一下串口的使用,CC2541有两个USART,使用都是类似的,根据使用管脚和串口的不同做适当的调整就好。

配置

我手头的是一个CC2541的最小系统,如下图所示,使用的串口是UART0,与IO的分配关系是:P1_5->TX,P1_4->RX。要实现串口的使用(不使用硬件流控制)需要进行IO的复用配置和针对串口的配置。

1

IO配置

需要将P1_4和P1_5两个IO进行复用配置,同时将UART0配置到这两个IO的位置,关于这一项配置,首先得明确IO复用的map,如下很清晰地看到UART0映射到P1_4和P1_5,是配置UART0为位置2。还需要配置相应管脚优先使用串口,那么需要配置的寄存器分别是PERCFG、P1SEL和P2DIR,关于这三个寄存器的详细信息可以看我的另一篇文章 BLE开发——CC2541寄存器整理

2

配置代码:

PERCFG |= 0x01;                          // 配置UART0为位置2
P1SEL = 0x3c;                            // P1_2,P1_3,P1_4,P1_5用作外设,串口功能
P2DIR &= ~0xc0;                          // P1优先用作串口

对了,这里得插一句,可能你不明白怎么看寄存器赋什么值,这里以上面的P1SEL为例讲一下:

我们知道此寄存器的描述:P1.7–P1.0的功能选择(0:通用I/O 1:外设功能)。我们需要的是P1_2,P1_3,P1_4,P1_5用作外设,所以这四个管脚对应的一个Byte的2、3、4、5bit位是1,其余bit位不管,设置为0为普通IO,所以值就是0011 1100那么对应十六进制数为0x3c。

UART0配置

IO配置好以后,需要对UART0的寄存器进行配置,首先得选择串口方式是UART异步通信方式,然后通过U0GCR.BAUD_E[4:0]和U0BAUD.BAUD[7:0]结合配置波特率,波特率的计算公式如下:

$$ BaudRate = \frac{(256+BAUD \_ M)\times2^{BAUD \_ E}}{2^{28}}\times f $$

其中f是系统时钟,我设置的是32MHz外部晶振,对应32MHz时钟波特率设置配置表如下:

波特率 UxBAUD.BAUD_M UxBAUD.BAUD_E 误差(%)
2400 59 6 0.14
4800 59 7 0.14
9600 59 8 0.14
14,400 216 8 0.03
19,200 59 9 0.14
28,800 216 9 0.03
38,400 59 10 0.14
57,600 216 10 0.03
76,800 59 11 0.14
115,200 216 11 0.03
230,400 216 12 0.03

其实还可以根据自己需求设置U0UCR对停止位、校验、起始位、位长进行配置,我没有配置使用了默认设置:8位数据位、没有校验、1个停止位。

配置代码:

U0CSR |= 0x80;                           // UART 方式
U0GCR |= 11;                             // U0GCR与U0BAUD配合
U0BAUD |= 216;                           // 波特率设为115200

接收中断配置

我采用接收中断的方式接收串口数据,所以还得需要配置中断,查询CC2541用户手册可以发现设置IEN0即可,寄存器详细如下:

4

设置第7位和低位即可,同时需要清空UART0串口接收中断标志,因为串口基本配置完毕,然后配置U0CSR允许接收,配置代码:

UTX0IF = 0;                              // 清除中断标志
U0CSR |= 0X40;                           // 允许接收
IEN0 |= 0x84;                            // 开总中断,接收中断

串口使用

这里采用中断的方式接收串口数据,所以必须写中断处理程序,接受搞定,那么发送数据怎么办呢?无论接收还是发送数据都是在操作U0DBUF,接收到数据后硬件会把数据放在U0DBUF,读取即可取出数据,当往U0DBUF写数据后,串口就会把数据发送出去,为了更方便使用串口,我编写了判断接收,读取数据,发送字符串、整数和浮点数的处理函数,下面分别来说一下。

串口接收中断

以下代码实现将接收到的数据发回去,将程序烧进芯片,连接串口到PC,在PC端打开串口助手(这个网上有很多,自己下载一个即可,比如OpenJumper串口助手)进行测试,PC端往串口发数据,则会看到CC2541将数据返回发送,PC端串口助手接收区便收到发送的数据。

// 中断处理
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
    unsigned char ch;
    URX0IF = 0;    // 清中断标志
    ch = U0DBUF;
    
    U0DBUF = ch;
}

串口函数

为了方便使用,写了一些函数,这里只说明一下接收:我是建立了一个缓存数组,长度64,还有一个标记指针,用来记录最新数据位置,每次接收到数据就往数组里塞,读取的时候取出数据,再逐个前移数据,标记指针减1。

具体实现,看我的代码,很好理解的。

  • 头文件serial.h

    #ifndef __SERIAL_H__
    #define __SERIAL_H__
    
    void oscInit(void);
    void serialInit(void);
    unsigned char serialAvailable(void);
    unsigned char serialRead();
    unsigned char serialWrite(unsigned char dataSend);
    void print(char *bytes, unsigned char length);
    void printInt(int num);
    void printFloat(float num);
    void println(char *bytes, unsigned char length);
    void printIntln(int num);
    void printFloatln(float num);
    
    #endif
    
  • 源文件serial.c

    #include "serial.h"
    #include <ioCC2541.h>
    
    static unsigned char serialReData[64];
    static unsigned char dataPoint = 0;
    
    // 启动外部32M晶振
    void oscInit(void) {
        SLEEPCMD &= ~0x04;                       // 启动所有晶振
        while (!(SLEEPSTA & 0x40));             // 等待晶振稳定
    
        CLKCONCMD = (CLKCONCMD & 0x80) | 0x49;   // 使用16M晶振作为主时钟
        while ((CLKCONSTA & ~0x80) != 0x49 );   // 等待主时钟切换到16M晶振
    
        CLKCONCMD = (CLKCONCMD & ~0x80) ;        // 使用外部32K晶振作为休眠时钟
        while ( (CLKCONSTA & 0x80) != 0 );      // 等待睡眠时钟切换到外部32K晶振
    
        CLKCONCMD = (CLKCONCMD & 0x80) ;        // 使用32M晶振作为主时钟
        while ( (CLKCONSTA & ~0x80) != 0 );     // 等待主时钟切换到32M晶振
    
        SLEEPCMD |= 0x04;                       // 关闭未使用的晶振
    }
    
    // 串口初始化
    void serialInit(void) {
        // 初始化串口的IO
        PERCFG |= 0x01;                          // 配置UART0为位置2
        P1SEL = 0x3c;                            // P1_2,P1_3,P1_4,P1_5用作串口功能
        P2DIR &= ~0xc0;                          // P1优先用作串口
        // 配置串口
        U0CSR |= 0x80;                           // UART 方式
        U0GCR |= 11;                             // U0GCR与U0BAUD配合
        U0BAUD |= 216;                           // 波特率设为115200
        UTX0IF = 0;                              // 清除中断标志
        U0CSR |= 0X40;                           // 允许接收
        IEN0 |= 0x84;                            // 开总中断,接收中断
    }
    
    unsigned char serialAvailable(void) {
        return dataPoint;
    }
    
    // 读取一个Byte
    unsigned char serialRead() {
        unsigned char ch;
        ch = serialReData[0];
        if(dataPoint > 0) {
            for(int i = 0; i < dataPoint; i++) serialReData[i] = serialReData[i+1];
            dataPoint--;
        } else {
            dataPoint = 0;
        }
        return ch;
    }
    
    // 发送一个Byte数据
    unsigned char serialWrite(unsigned char dataSend) {
        U0DBUF = dataSend;
        while((U0CSR & 0x01) == 0x01);
        return 1;
    }
    // 按个数发送字符串
    void print(char *bytes, unsigned char length) {
        for(int i = 0; i < length; i++) {
            serialWrite(*(bytes+i));
        }
    }
    // 发送整数
    void printInt(int num) {
        if(num == 0) {
            serialWrite(0x30);
        } else {
            unsigned char ch;
            unsigned int nT;
            unsigned char flag = 0;
            if(num < 0) {
                serialWrite('-');
                nT=-num;
            } else {
                nT = num;
            }
            for(int i = 0; i < 5; i++) {
                ch = nT/10000+0x30;
                nT %= 10000;
                nT *= 10;
                if(flag == 1) {
                    serialWrite(ch);
                } else {
                    if(ch > 0x30) {
                        flag = 1;
                        serialWrite(ch);
                    }
                }
            }
        }
    }
    
    // 发送两位小数
    void printFloat(float numFloat) {
        int numI = (int)numFloat;
        unsigned char factor = 100;
        char ch;
        printInt(numI);
        numI = (int)(numFloat*factor-numI*factor);
        serialWrite('.');
        ch = numI / 10;
        serialWrite(ch+0x30);
        ch = numI % 10;
        serialWrite(ch+0x30);
    }
    
    // 发送字符串(带换行)
    void println(char *bytes, unsigned char length) {
        print(bytes, length);
        serialWrite('\n');
    }
    // 发送整数(带换行)
    void printIntln(int num) {
        printInt(num);
        serialWrite('\n');
    }
    // 发送两位小数(带换行)
    void printFloatln(float num) {
        printFloat(num);
        serialWrite('\n');
    }
    
    // 中断处理
    #pragma vector = URX0_VECTOR
    __interrupt void UART0_ISR(void)
    {
        unsigned char ch;
        URX0IF = 0;    // 清中断标志
        ch = U0DBUF;
        if(dataPoint < 64) {
            serialReData[dataPoint] = ch;
            dataPoint++;
        } else {
            for(int i = 0; i < 63; i++) {
                serialReData[i] = serialReData[i+1];
            }
            dataPoint = 63;
            serialReData[63] = ch;
        }
        U0DBUF = ch;
    }
    

bleuart

2434 字

2015-10-30 14:00 +0800