预备知识
硬件
RS232电平:负逻辑电压(负电平表示逻辑1,正电压表示逻辑0),12v,电脑使用居多
TTL/CMOS电平:正逻辑电压(正负电压分别表示逻辑1和0),5v,单片机等嵌入式设备使用较多
(RS232多为9孔插槽,TTL/CMOS电平设备需要使用转换芯片才能与RS232电平设备通信,一般该芯片集成到嵌入式设备中,以9孔232插槽作为硬件端口,但是现在使用变少,慢慢由USB取代,因此需要的芯片变为TTL/CMOS转USB芯片)
TXD、RXD、GND:TXD为发送引脚,RXD为接收引脚,发送端和接受端的TX和RX应该以RX-TX的连接方式连接。GND用于设备共地,保证逻辑电平的一致(电势有参考才能以电压论述)。
软件
数据格式:起始位,数据位,校验位,停止位
串口协议数据异步传输协议,每一次传输需要由额外的信息来发起一次传输同步。通常这种同步格式以起始位和停止位来体现。同时为了保证数据的正确性,串口协议还可以存在校验位来判断本次传输的数据是否正确,一般常用为奇偶校验,也可以有其他方式校验。剩下的几位数据位。
串口协议空闲情况下TXD和RXD被上拉电阻拉高,体现为高电平,当从高电平置为低电平,则视为一个起始位( 0 ),而停止位则为相反的从低电平转为高电平( 1 )。一般情况下,使用的是8n1,即8个数据位,1个起始位和1个停止位,没有校验位,即发送1Byte数据需要发送10bits发送。
波特率:接收端和发送端协商好的发送数据的速率,含义为1s内发送的数据位数。
分析
以S3C2440的串口框图为例,需要发送数据时,数据将从内存中通过外设总线传入TransmitBufferRegister即传输缓存寄存器中,然后发送移位寄存器会将缓冲区中待发送的数据逐位发送。接收则是通过接收移位寄存器将接收到的数据逐位保存到缓冲区中,等待提取。移位寄存器的速率受波特率发生器影响,这个就是上文中提到的我们需要提前订好的发送或者接收速率,需要发送端和接收端都一致才能保证信息的正确性。
程序
和往常一样,需要使用到一样功能,需要看原理图找到相应的引脚,然后查找寄存器,配置寄存器,使能功能,再加上业务逻辑完成期望功能。原理图中看出板载有USB转uart的电路,连接的是TXD0和RXD0,分别连接到GPH2和GPH3引脚,因此需要将这两个引脚的UART功能使能,再配置uart的相关寄存器。
将uart需要用到的寄存器添加到s3c2440.h中,方便以后复用。
s3c2440.h
--------------------------------------------------------------
#ifndef __S3C2440_H
#define __S3C2440_H
#include <stdint.h>
/* CLOCK --------------------------------------------------*/
#define MPLLCON (*((volatile uint32_t*)0x4C000004))
#define UPLLCON (*((volatile uint32_t*)0x4C000008))
#define CLKDIVN (*((volatile uint32_t*)0x4C000014))
/* WATCH DOG --------------------------------------------------*/
#define WTCON (*((volatile uint32_t*)0x53000000))
/* GPIO --------------------------------------------------*/
#define GPGCON (*((volatile uint32_t*)0x56000060))
#define GPGDAT (*((volatile uint32_t*)0x56000064))
#define GPGUP (*((volatile uint32_t*)0x56000068))
#define GPFCON (*((volatile uint32_t*)0x56000050))
#define GPFDAT (*((volatile uint32_t*)0x56000054))
#define GPFUP (*((volatile uint32_t*)0x56000058))
#define GPHCON (*((volatile uint32_t*)0x56000070))
#define GPHDAT (*((volatile uint32_t*)0x56000074))
#define GPHUP (*((volatile uint32_t*)0x56000078))
/* UART --------------------------------------------------*/
//uart0
#define ULCON0 (*((volatile uint32_t*)0x50000000))
#define UCON0 (*((volatile uint32_t*)0x50000004))
#define UFCON0 (*((volatile uint32_t*)0x50000008))
#define UMCON0 (*((volatile uint32_t*)0x5000000C))
#define UTRSTAT0 (*((volatile uint32_t*)0x50000010))
#define UERSTAT0 (*((volatile uint32_t*)0x50000014))
#define UFSTAT0 (*((volatile uint32_t*)0x50000018))
#define UMSTAT0 (*((volatile uint32_t*)0x5000001C))
#define UBRDIV0 (*((volatile uint32_t*)0x50000028))
#define UTXH0 (*((volatile uint8_t*)0x50000020))
#define URXH0 (*((volatile uint8_t*)0x50000024))
//uart1
#define ULCON1 (*((volatile uint32_t*)0x50004000))
#define UCON1 (*((volatile uint32_t*)0x50004004))
#define UFCON1 (*((volatile uint32_t*)0x50004008))
#define UMCON1 (*((volatile uint32_t*)0x5000400C))
#define UTRSTAT1 (*((volatile uint32_t*)0x50004010))
#define UERSTAT1 (*((volatile uint32_t*)0x50004014))
#define UFSTAT1 (*((volatile uint32_t*)0x50004018))
#define UMSTAT1 (*((volatile uint32_t*)0x5000401C))
#define UBRDIV1 (*((volatile uint32_t*)0x50004028))
#define UTXH1 (*((volatile uint8_t*)0x50004020))
#define URXH1 (*((volatile uint8_t*)0x50004024))
//uart2
#define ULCON2 (*((volatile uint32_t*)0x50008000))
#define UCON2 (*((volatile uint32_t*)0x50008004))
#define UTRSTAT2 (*((volatile uint32_t*)0x50008010))
#define UERSTAT2 (*((volatile uint32_t*)0x50008014))
#define UFSTAT2 (*((volatile uint32_t*)0x50008018))
#define UBRDIV2 (*((volatile uint32_t*)0x50008028))
#define UTXH2 (*((volatile uint8_t*)0x50004020))
#define URXH2 (*((volatile uint8_t*)0x50008024))
void HardwareInitAll(void);
void Delay(uint32_t time);
#endif
配置GPIO为RXD和TXD功能,并配置UART相关寄存器,将uart的通信设置为8位数据位,无校验位,1位停止位,使用PCLK(50MHz)作为uart时钟,并由波特率为115200代入参考手册提供的公式,得到uart时钟分频。如下:
uart.h
----------------------------------------------------
#ifndef __UART_H
#define __UART_H
#include <stdint.h>
void UartInit(void);
void putc(uint8_t character);
uint8_t getc(void);
#endif
uart.c
------------------------------------------
#include "uart.h"
#include "s3c2440.h"
void UartInit(void)
{
//GPIO init
GPHCON &= ~((3<<6) | (3<<4));
GPHCON |= (2<<6) | (2<<4);
//UART init
ULCON0 = 3; //8n1
UCON0 = (1<<2) | (1<<0);
UBRDIV0 = (int32_t)( 50000000/ (115200 * 16) ) -1;
}
static uint8_t IsReadyToSend(void)
{
return UTRSTAT0 & (1<<1);
}
static uint8_t IsReadyToReceive(void)
{
return UTRSTAT0 & (1<<0);
}
void putc(uint8_t character)
{
while(!IsReadyToSend());
UTXH0 = character;
if(character==(uint8_t)('\r'))
{
while(!IsReadyToSend());
UTXH0 = (uint8_t)'\n';
}
}
uint8_t getc(void)
{
while(!IsReadyToReceive());
return URXH0;
}
同时将状态寄存器中的相关状态位封装成指定功能的函数,增加需要的逻辑,得到最终的业务代码:putc和getc,效果为,使用串口工具进行通信,输入字符即可回显。
main.c
--------------------------------------------
#include <stdint.h>
#include "s3c2440.h"
#include "led.h"
#include "uart.h"
int main()
{
HardwareInitAll();
UartInit();
uint8_t tmp;
while(1)
{
tmp = getc();
putc(tmp);
}
}
至此,一个简单粗糙的串口回显程序完成。