串口概述

串口是用于串口行传递数据的接口,在嵌入式设备中,串口还经常作为调试终端,用于打印各种调试信息。串口通信分为同步通讯和异步通信,通信时,双方需要先约定好数据帧的格式,即波特率,数据位,停止位,奇偶校验位等。
Alt text

波特率用于衡量通信速度,它表示信号被调制以后在单位时间内的变化次数。波特率与比特率容易混淆,后者是对信息传输速率的度量,单位是bps。波特率可以被理解为单位时间内传输符号的个数(传符号率),通过不同的调制方法可以在一个符号上负载多个比特信息。以RS232为例,常见的波特率有38400,115200,对应的比特率就是38400bps和115200bps。

除了波特率外,串口通讯还有其他的参数,包括以下几个:
起始位:当线路空闲时,电平为高。一旦检测到一个下降沿,则视为一个起始位,然后接收方按照约定好的格式,接收这一帧数据。
数据位:一帧中实际有效数据的位数。
停止位:表示这帧数据的结束。
校验位:用于检测数据传输是否正确的位。

串口有许多标准,以通常使用的RS232的9针串口为例,其中最为重要的是2,3,5脚:
Alt text
2:RXD:数据接收端
3:TXD:数据发送端
5:GND:接地

串口初始化

首先查找OK6410的原理图,查看串口的硬件连接:
Alt text
Alt text

由硬件原理图可知,COM0使用的串口引脚是GPA0和GPA1,那么首先就需要找到GPA组的寄存器,把这两个引脚配置成串口功能来使用,寄存器描述如下:
Alt text

接下来是对串口的数据格式的配置,比如配置串口的数据位,停止位,奇偶校验位等信息,相关的寄存器是ULCON0。需要按8位数据位,1位停止位,无奇偶校验方式进行配置:
Alt text

接下来是设置串口的工作模式,串口收发可以工作在多种模式,比如DMA模式,中断模式,轮询模式,在有操作系统的情况下,串口一般会工作在中断模式或是DMA模式,但是此处我们将其设置为轮询模式。相关的配置寄存器是UCON0的bit0~3Alt text

接下来是设置波特率,通过寄存器UBRDIV0和UDIVSLOT0来控制,设置方法如下:
Alt text
Alt text

接下来实现字符的发送与接收,首先通过寄存器UTRSTAT0来判断收和发队列的状态,判断队列为空或为满,然后在条件满足的情况下,访问各自的数据缓冲寄存器即可。
Alt text

综上,编写串口的测试代码:

#define GPACON         (*((volatile unsigned short *)0x7F008000))
#define ULCON0         (*((volatile unsigned long *)0x7F005000))
#define UCON0          (*((volatile unsigned long *)0x7F005004))
#define UTRSTAT0       (*((volatile unsigned long *)0x7F005010))
#define UBRDIV0        (*((volatile unsigned short *)0x7F005028))
#define UTXH0          (*((volatile unsigned char *)0x7F005020))
#define URXH0          (*((volatile unsigned char *)0x7F005024))
#define UDIVSLOT0      (*((volatile unsigned short *)0x7F00502C))

#define PCLK 66500000
#define BAUD 115200

void uart_init()
{
    //1.配置引脚功能
    GPACON &= ~0xff;
    GPACON |= 0x22;
    
    //2.1 设置数据格式
    ULCON0 = 0b11;
    
    //2.2 设置工作模式
    UCON0 = 0b0101; 
    
    //3. 设置波特率    
    UBRDIV0 =(int)(PCLK/(BAUD*16)-1);  //UBRDIV0保存该公式计算后的整数部分
    UDIVSLOT0 = 0x0;   //UDISLOT0=保存该公式计算后的小数部分*16
}


void putc(unsigned char ch)
{
    while (!(UTRSTAT0 & (1<<2)));
    UTXH0 = ch;  
}


unsigned char getc(void)
{
    unsigned char ret;

    while (!(UTRSTAT0 & (1<<0)));
    // 取数据
    ret = URXH0;  
    
    if ( (ret == 0x0d) || (ret == 0x0a) )
    {
        putc(0x0d);
        putc(0x0a);    
    }          
    else
        putc(ret);
        
        return ret;
}

建立串口控制台

下一步在OK6410上实现一个菜单式的控制台,用户可以根据菜单提示选择要进行何种操作,控制台的主程序代码如下所示:

    while(1)
    {
        printf("********************************\n\r");
        printf("*************GBOOT**************\n\r");
        printf("1:Download kernel from tftp server\n\r");
        printf("2:Boot linux from RAM\n\r");
        printf("3:Boot linux from NAND\n\r");
        printf("pleare enter your selection:");
        
        scanf("%d", &num);
        
        switch(num)
        {
            case 1:
            //tftp_download();
                break;
            case 2:
            //boot_linux_ram();
                break;
            case 3:
            //boot_linux_nand();
                break;
            default:
            printf("Error: wrong selection!\n\r");
                break;
        }
    }

为此,需要实现printf和scanf函数,才能方便程序的编写。

实现printf/scanf

printf和scanf的实现需要借助标准C中的其他与硬件无关的库函数,比如vsprintf和vsscanf,有了这些函数,printf与scanf只需要实现与硬件相关的字符输入与输出即可,格式转化及变参处理部分交由库函数进行处理。最终printf和scanf的实现如下:

#include "vsprintf.h"

unsigned char outbuf[1024];
unsigned char inbuf[1024];

int printf(const char *fmt, ...)
{
    int i;
    
    //1.将变参转化为字符串
    va_list args;
    va_start(args, fmt);
    vsprintf((char*)outbuf, fmt, args);
    va_end(args);
    
    
    //2.打印字符串到串口
    for(i = 0; i < strlen(outbuf); i++)
    {
        putc(outbuf[i]);
    }
    
    return i;
}

int scanf(const char *fmt, ...)
{
    va_list args;
    unsigned char c;
    int i = 0;
    
    //1.获取输入的字符串
    while(1)
    {
        c = getc();
        if(c == 0x0a || c == 0x0d)
        {
            inbuf[i] = '\n';
            break;
        }
        else
        {
            inbuf[i] = c;
            ++i;
        }
    }
    
    //2.格式转化
    va_start(args, fmt);
    vsscanf((char *)inbuf, fmt, args);
    va_end(args);
    
    return i;
}

  • 无标签