一、串口通信的基本原理
串口通信的基本原理编辑
串口在嵌入式系统当中是一类重要的数据通信接口,其本质功能是作为 CPU 和串行设备间的编码转换器。当数据从 CPU 经过串行端口发送出去时,字节数据转换为串行的位;在接收数据时,串行的位被转换为字节数据。应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请要求(打开串口),通信完成后必须释放资源(关闭串口)。典型地,串口用于 ASCII 码字符的传输。通信使用3根线完成:(1)地线,(2)发送数据线,(3)接收数据线。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:波特率是一个衡量通信速度的参数,它表示每秒钟传送的 bit 的个数;数据位是衡量通信中实际数据位的参数,当计算机发送一个信息包,标准的值是 5,7 和 8 位。如何设置取决于你的需求;停止位用于表示单个包的最后一位,典型的值为 1,1.5和 2 位,停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会;奇偶校验位是串口通信中一种简单的检错方式,有四种检错方式——偶、奇、高和低,也可以没有校验位。
二、iTOP4412串口的原理图
配置串口之前首先应配置时钟树 因为波特率的计算需要知道时钟频率
三、UART时钟配置
从上图可知UART在PERI-L模块中 如果要PERI-L则需要先配置DMC时钟然后在配置左总线时钟
由上图可知PERIL时钟频率的来源为SCKLmpll,但是SCKLmpll的时钟频率又是来自哪里呢?
根据该图可知SCLKmpll的时钟频率来自MPLL那么我们还需要配置MPLL MPLL时钟频率为800HZ
DRAM, system bus clocks, and other peripheral clocks like audio IPs, and SPI use MPLL and EPLL
DRAM,系统总线时钟以及其他外围设备时钟(如音频IP和SPI)使用MPLL和EPLL
根据这个描述 可以知道 UART使用的是MPLL和EPLL作为时钟输入源
We recommend using 24 MHz input clock source for APLL, MPLL, EPLL, and VPLL
我们建议为APLL,MPLL,EPLL和VPLL使用24 MHz输入时钟源。
而MPLL的时钟输入源为24MHZ源晶振 这显然是不足的
根据这个表中的信息 设置 P M S 便可以 将MPLL调频至指定值
找到相关相关寄存器
接下来就是要找到需要配置的寄存器即可
因为需要配置的是UART 先从最下游开始依次找到上游 然后再由上游向下游依次配置
需要配置各个串口需要设置 PERIL中的分频器 UART的时钟频率来自 SCLKMPLL_UAER_T
而SCLKMPLL_UAER_T 的时钟源来自SCLKMPLL
配置寄存器
1、首先是将MPLL升频至800MHz 需要配置的寄存器
下面是计算方法 我们只需要根据上面的表中配置手册中推荐的值即可 即将MPLL_CON0 (0x10040108) =
0x80640300 即可
2、接下来是配置SCLKmpll
由于SCLKmpll的时钟源来自 DMC时钟树下 则需要配置的寄存器为
即 将 CLK_SRC_DMC 0x10040200 = 0x00000100 即可
接下来便是选定SCLKMPLL作为PERIL的时钟源 该模块在TOP时钟树下
需要配置的寄存器为
CLK_SRC_TOP1 0x1003C214 = 0x00001000 选定SCLKMPLL作为时钟源
配置 UART 时钟 UART时钟源为 SCLKMPLL_USER-T 和 SCLKMPLL时钟频率相同都为800MHz 再经过DIVuart 分频器 将800MHz的时钟频率8分频为100MHz
涉及到的寄存器为
设置UATR的时钟源
0x1003C250 = 0x66666
设置分频器为8分频 分别作用于UART
0x1003C550 = 0x77777
设置SCL_SRC_PERIL0 0x1003C250 = 0x66666
设置分频比为8
CLK_DIV_PERIL0 0x1003C550 = 0x77777
时钟配置完毕
接下来进行UART配置
UART初始化
-
数据格式控制
ULCON:8bit、停止位、奇偶校验
-
整个uart控制器的控制
UCON:
-
配置波特率
UBRDIV UDIVSLOT
UART接收
-
数据收发缓存器
UTXH URXH
-
数据收发的状态
UTRSTAT specifies Tx/Rx status 指定transmit/receive的状态
串口配置相对简单查看芯片手册即可
源码
UART.h
#ifndef __UART_H
#define __UART_H
union br_rest {
unsigned short slot; /* udivslot */
unsigned char value; /* ufracval */
};
struct s5p_uart {
unsigned int ulcon;
unsigned int ucon;
unsigned int ufcon;
unsigned int umcon;
unsigned int utrstat;
unsigned int uerstat;
unsigned int ufstat;
unsigned int umstat;
unsigned char utxh;
unsigned char res1[3];
unsigned char urxh;
unsigned char res2[3];
unsigned int ubrdiv;
union br_rest rest;
unsigned char res3[0xffd0];
};
/*GPIO*/
#define GPA1CON (*(volatile int *) 0x11400020)
#define GPADAT (*(volatile int *) 0x11400024)
/*uart*/
#define UART1_BASE (*(volatile int *) 0x13820000)
#define UART1_ULCON (*(volatile int *) 0x13820000)
#define UART1_UCON (*(volatile int *) 0x13820004)
#define UART1_DIV (*(volatile int *) 0x13820028)
#define UART1_UFRACVAL (*(volatile int *) 0x1382002C)
#define UTXH (*(volatile int *) 0x13820020)
#define URXH (*(volatile int *) 0x13820024)
#define UTRSTAT (*(volatile int *) 0x13820010)
/*MPLL*/
#define CLK_SRC_DMC (*(volatile int *) 0x10040200)
#define MPLL_CON0 (*(volatile int *) 0x10040108)
#define CLK_SRC_TOP1 (*(volatile int *) 0x1003C214)
//uart clock
#define CLK_DIV_PERIL0 (*(volatile int *) 0x1003C550)
#define CLK_SRC_PERIL0 (*(volatile int *) 0x1003C250)
void clock(void);
void uart(void);
void myput(char c);
void mystring(char *str);
#endif
start.S
.global _start
.global main
_start:
bl main
.end
uart.c
#include"UART.h"
#include"cpu_io.h"
#include"led.h"
void clock(void)
{
// 设置MPLL
CLK_SRC_DMC = 0x01000; //配置MUXMPLL 使用FOUT
MPLL_CON0 = 0x80640300; // M 100 P 3 S 0;
CLK_SRC_TOP1 = 0x01000;
CLK_SRC_PERIL0 = 0x66666;
CLK_DIV_PERIL0 = 0x77777;
}
void uart(void)
{
GPA1CON = 0x00000022; //设置GPIO
/* 串口初始化 */
UART1_ULCON = 0x00000003;
UART1_UCON = 0x00000005;// 设置接收和发送模式为中断或轮询
UART1_DIV = 53;
UART1_UFRACVAL = 4;
}
/*设置receive transmits buff*/
void myput(char c)
{
/*
等待当前发送完成
然后再发送下次的数据
*/
while (!((UTRSTAT) & (0x1<<2))); //第二位为1的时候表示为空 为0 表示非空
UTXH = c;
}
void mystring(char *str)
{
while(*str)
{
myput(*str);
str++;
}
}
led.c
/*************************************************************************
> File Name: led.c
> 作者:YJK
> Mail: 745506980@qq.com
> Created Time: 2020年09月19日 星期六 18时28分16秒
************************************************************************/
#include"led.h"
void desplay(void)
{
volatile int x = 0xFFFF;
while (x--);
}
void led(void)
{
GPL2CON = 0x1;
GPK1CON = 0x10; /*设置为输出模式*/
/* while(1)
{
GPL2DAT = 0x1;
GPK1DAT = 0x2;
desplay();
GPL2DAT = 0;
GPK1DAT = 0;
desplay();
} */
GPL2DAT = 0x1;
GPK1DAT = 0x2;
}
main.c
/*************************************************************************
> File Name: main.c
> 作者:YJK
> Mail: 745506980@qq.com
> Created Time: 2020年09月04日 星期五 17时53分51秒
************************************************************************/
#include"UART.h"
#include"led.h"
int main(int argc,char *argv[])
{
clock();
uart();
myput('h');
myput('h');
mystring("hello world\n");
led();
return 0;
}
map.lds
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x0;
. = ALIGN(4);
.text :
{
start.o
*(.text)
}
. = ALIGN(4);
.rodata :
{
*(.rodata)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
.bss :
{
*(.bss)
}
}
mk4412.c
/*************************************************************************
> File Name: mk4412.c
> 作者:YJK
> Mail: 745506980@qq.com
> Created Time: 2020年09月21日 星期一 09时24分15秒
************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE (8 * 1024)
#define HEADER_SIZE (16)
int main(int argc, char *argv[])
{
FILE *fp;
unsigned char buffer[BUFFER_SIZE];
unsigned char header[HEADER_SIZE];
unsigned int checksum, count;
int i, len;
if (argc != 2) {
printf("Usage: mk4412 <file>\n");
return -1;
}
fp = fopen(argv[1], "r+b");
if (fp == NULL) {
printf("Can not open file '%s'\n", argv[1]);
return -1;
}
fseek(fp, 0L, SEEK_END);
len = ftell(fp);
count = (len < BUFFER_SIZE) ? len : BUFFER_SIZE;
fseek(fp, 0L, SEEK_SET);
memset(buffer, 0, sizeof(buffer));
if (fread(buffer, 1, count, fp) != count) {
printf("Can't read %s\n", argv[1]);
fclose(fp);
return -1;
}
for (i = 16, checksum = 0; i < count; i++) {
checksum += buffer[i] & 0xff;
}
memset(header, 0, sizeof(header));
header[3] = (0x1f >> 24) & 0xff;
header[2] = (0x1f >> 16) & 0xff;
header[1] = (0x1f >> 8) & 0xff;
header[0] = (0x1f >> 0) & 0xff;
header[7] = (checksum >> 24) & 0xff;
header[6] = (checksum >> 16) & 0xff;
header[5] = (checksum >> 8) & 0xff;
header[4] = (checksum >> 0) & 0xff;
header[0] ^= 0xbc;
header[1] ^= 0xca;
header[2] ^= 0xba;
header[3] ^= 0xcb;
header[4] ^= 0xcb;
header[5] ^= 0xce;
header[6] ^= 0xcd;
header[7] ^= 0xdf;
header[8] ^= 0xb7;
header[9] ^= 0xba;
header[10] ^= 0xbe;
header[11] ^= 0xbb;
header[12] ^= 0xba;
header[13] ^= 0xad;
header[14] ^= 0xdf;
header[15] ^= 0xdf;
for (i = 1; i < HEADER_SIZE; i++) {
header[i] ^= header[i - 1];
}
fseek(fp, 0L, SEEK_SET);
if (fwrite(header, 1, sizeof(header), fp) != sizeof(header)) {
printf("Write header error %s\n", argv[1]);
fclose(fp);
return -1;
}
fclose(fp);
printf("The checksum is 0x%08x for %ld bytes [0x%lx ~ 0x%x]\n",
checksum, (count - sizeof(header)), sizeof(header), (count - 1));
return 0;
}
Makefile
#######################################################################
# File Name: Makefile
# 作者:YJK
# mail: 745506980@qq.comc
# Created Time: 2020年09月17日 星期四 14时23分10秒
#########################################################################
# $^ 当前规则中的所有依赖
# $< 当前规则中的第一个依赖
# $@ 当前规则中触发命令生成的目标
# @ 不把执行的信息打印到显示屏上
#工程Makefile
#define var 定义变量
#.c ---- .o ------ build(elf文件)------build.bin
TARGET := uart.bin
BUILD := uart
ENV_ ?= SD#?=如果不指定 则按默认方式
COBJS += main.o#+= 累加
COBJS += uart.o led.o
COBJS += start.o
CROSS_COMPILE := arm-none-linux-gnueabi-
#工具集
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld#链接器
OBJCOPY := $(CROSS_COMPILE)objcopy#文件格式的转换工具
SDTOOLS := ./mk4412
#编译选项
CFLAGS += -Wall #编译时的选项
#CFLAGS += -I/usr/local/arm/opt/FriendlyARM/toolschain/4.4.3/include/ #寻找include 头文件的选项 -I/include路径
#查看启动方式
LDFLAGS += -Tmap.lds
#LDFLAGS += $(call lmZ-option, --no-dynamic-linker)
ifeq ($(ENV_),SD)
LDFLAGS += -Ttext=0x02021400
else
LDFLAGS += -Ttext=0x20000000
endif
#链接脚本
#1、概念 告诉链接器如何工作的一个文本文档
#1.o 2.o 3.o ---->build
#2 、要素
# 1、哪一个.o放到代码段的起始位置
#连接器没有指定的方式 需要使用连接脚本
# 2、所有的.o放到哪个基地址上
#方式 ld -Ttext=xxxx 简单链接指定了代码段的基地址 代码段 数据段等是连续的
#代码段、数据段不连续的方式 使用 .lds文件
#
#Way 方法
all:$(TARGET)
ifeq ($(ENV_), RAM)
$(TARGET):$(BUILD)
$(OBJCOPY) -O binary $^ $@
else
$(TARGET):$(BUILD)
$(OBJCOPY) -O binary $^ $@
#生成一个临时文件 因为以SD卡启动需要在头部增加一个四字节的检测位 i
$(SDTOOLS) $@
endif
$(BUILD):$(COBJS)
$(LD) $(LDFLAGS) $^ -o $@
%.o:%.c #任意的.c 想生成任意的.o 使用的是下面的规则 其中.o 文件名是相应的.c文件名
$(CC) -c $< -o $@ $(CFLAGS)
%.o:%.S
$(CC) -c $< -o $@ $(CFLAGS)
clean:
rm $(BUILD) *.o $(TARGET)
flash:
dd if=/dev/zero of=/dev/sdb bs=512 seek=1 iflag=dsync oflag=dsync count=2048
# dd if=./E4412_N.bl1.SCP2G.bin of=/dev/sdb bs=512 seek=1 iflag=dsync oflag=dsync
dd if=./$(TARGET) of=/dev/sdb bs=512 seek=1 iflag=dsync oflag=dsync
install:
编译–烧录
make
make flash 烧录到SD卡
上电