STC15单片机串口打印
实现功能
1.上电时,通过TTL转USB接口输出系统启动信息
2.运行时,间隔100ms输出hello字符串和一个自动加1的变量
STC15L2K32S2型号串口配置
串口的使用跟之前STC89C52的一样,只不过这个型号具有两个串口,都是UART(通用异步收发器),分别在两组不同的引脚上
串口1建议放在[P3.6/RxD_2,P3.7/TxD_2]或[P1.6/RxD_3/XTAL2,P1.7/TxD_3/XTAL1]上。
寄存器的配置可用STC-ISP软件生成,也可根据手册进行配置,最后记得打开中断
程序
文件结构
main.c ->主函数文件,包含main函数等;
Public.c ->公共函数文件,包含Delay延时函数等;
Sys_init ->系统初始化函数,包含GPIO初始化函数等;
LED.c->LED外设函数,包含LED打开、关闭函数等;
Timer0.c ->定时器函数,包含定时器初始化,中断函数等;
KEY1.c->按键1函数,包含按键检测,中断函数等;
KEY2.c ->按键⒉函数,包含按键状态机检测函数等;
PWM. c ->PWM初始化、亮度调节、占空比储存与恢复函数等;
IAP.c->字节读、字节写、扇区擦除等函数。
UART1.c->串口1初始化、发送、中断等函数。
UART.h:
主要是定义波特率的枚举类型,之前用串口都是程序写死波特率,要改的话又要用软件生成一次,这次的灵活点,定义该波特率枚举类型后在后面可以用switch语句列出这四种波特率的TH1、TL1的初始值,在使用时,直接修改波特率的变量的值即可,非常方便
然后就是定义接口类型的枚举,再对串口的结构体进行定义,函数指针是等UART.c里的函数实现后,把名字复制过来的
#ifndef __UART_H_
#define __UART_H_
//定义波特率的枚举类型
typedef enum
{
Band_4800 = (uint8_t)0,
Band_9600 = (uint8_t)1,
Band_19200 = (uint8_t)2,
Band_115200 = (uint8_t)3
}BandRate_t;
//定义接口类型的枚举类型
typedef enum
{
TTL = (uint8_t)0,
RS_485 = (uint8_t)1,
RS_232 = (uint8_t)2
}Interface_Type_t;
//定义异步通信串口结构体类型
typedef struct
{
BandRate_t ucBandRate; //波特率
uint8_t volatile ucTX_Busy_Flag; //发送忙碌标志
uint8_t volatile ucRec_Flag; //接收标志位
uint8_t volatile ucRec_Cnt; //接收计数
uint8_t *pucSend_Buffer; //发送缓存指针
uint8_t *pucRec_Buffer; //接收缓存指针
void (*UART_Init)(); //串口初始化
void (*UART_SendData)(uint8_t); //串口发送字符
void (*UART_SendArray)(uint8_t*,uint16_t); //串口发送数组
void (*UART_SendString)(uint8_t *); //串口发送字符串
void (*Protocol)(); //接口协议
uint8_t Interface_Type; //接口类型
void (*RS485_Set_SendMode)(); //RS-485接口设置为发送模式
void (*RS485_Set_RecMode)(); //RS-485接口设置为接收模式
}UART_t;
/* extern variables-----------------------------------------------------------*/
/* extern function prototypes-------------------------------------------------*/
#endif
/********************************************************
End Of File
********************************************************/
UART1.h:
主要是先宏定义后续会学到的RS-485接口的单片机引脚,然后就把UART.h中的结构体变量声明为外部变量
#ifndef __UART1_H_
#define __UART1_H_
//串口引脚宏定义
#define UART1_TX P37 //这两个引脚接到RS-485
#define UART1_RX P36
/* extern variables-----------------------------------------------------------*/
extern UART_t idata UART1;
/* extern function prototypes-------------------------------------------------*/
#endif
/********************************************************
End Of File
********************************************************/
UART1.c:
与串口相关的分别是4个函数,串口初始化,串口发送字符,串口发送数组,串口发送字符串,另外对putchar函数进行重写,实现printf函数的重定向,为什么这里修改putchar函数而不是fputc函数,可以看之前的的文章(串口打印-printf重定向)
/* Includes ------------------------------------------------------------------*/
#include <main.h>
/* Private define-------------------------------------------------------------*/
#define UART1_Send_LENGTH 20
#define UART1_Rec_LENGTH 10
/* Private variables----------------------------------------------------------*/
static uint8_t idata ucSend_Buffer[UART1_Rec_LENGTH] = {0};
static uint8_t idata ucRec_Buffer[UART1_Rec_LENGTH] = {0x00};
/* Private function prototypes------------------------------------------------*/
static void Init(); //串口初始化
static void SendData(uint8_t dat); //串口发送字符
static void SendArray(uint8_t *p_Arr,uint16_t LEN); //串口发送数组
static void SendString(uint8_t *p_Str); //串口发送字符串
static void Protocol(); //串口协议
static void RS485_Set_SendMode(); //RS-485设置为发送模式
static void RS485_Set_RecMode(); //RS-485设置为接收模式
/* Public variables-----------------------------------------------------------*/
UART_t idata UART1 =
{
Band_115200,
FALSE,
FALSE,
0,
ucSend_Buffer,
ucRec_Buffer,
Init,
SendData,
SendArray,
SendString,
Protocol,
TTL,
RS485_Set_SendMode,
RS485_Set_RecMode
};
/*
* @name Init
* @brief 串口1初始化
* @param None
* @retval None
*/
static void Init()
{
//串口1默认映射至P30,P31,因为切换寄存器AUXR1上电默认值为0x00,默认串口1在P3.0和P3.1
SCON = 0x50; //8位数据,可变波特率,REN位置1,开启中断
//辅助寄存器AUXR的第6位T1x12置1,设置定时器1的速度是传统8051的12倍,不分频
AUXR |= 0x40;
//1111 1110 最低位S1ST2清0,选择定时器1作为串口1的波特率发生器
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设定定时器1为16位自动重装方式
switch (UART1.ucBandRate)
{
case Band_4800: TL1 = 0xCD; TH1 = 0xFD; break;
case Band_9600: TL1 = 0xE0; TH1 = 0xFE; break;
case Band_19200: TL1 = 0x70; TH1 = 0xFF; break;
case Band_115200: TL1 = 0xE8; TH1 = 0xFF; break;
default: TL1 = 0xCD; TH1 = 0xFD; break;
}
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
/*
* @name SendData
* @brief 发送字符
* @param dat:待发送的数据
* @retval None
*/
static void SendData(uint8_t dat)
{
while(UART1.ucTX_Busy_Flag); //等待前面的数据发送完,串口中断中发送数据后标志位会置FALSE
UART1.ucTX_Busy_Flag = TRUE; //置为忙碌标志位
SBUF = dat; //写数据到UART寄存器
}
/*
* @name SendArray
* @brief 发送数组
* @param p_Arr:数组首地址,LEN:发送长度
* @retval None
*/
static void SendArray(uint8_t *p_Arr,uint16_t LEN)
{
uint16_t i = 0;
for(i = 0;i<LEN;i++)
{
UART1.UART_SendData(*(p_Arr+i));
}
while(UART1.ucTX_Busy_Flag);
}
/*
* @name SendString
* @brief 发送字符串
* @param p_Arr:字符串首地址
* @retval None
*/
static void SendString(uint8_t *p_Str)
{
while(*(p_Str) != '\0')
{
UART1.UART_SendData(*(p_Str++));
}
while(UART1.ucTX_Busy_Flag);
}
/*
* @name RS485_Set_SendMode
* @brief RS_485接口设置为发送模式
* @param None
* @retval None
*/
static void RS485_Set_SendMode()
{
}
/*
* @name RS485_Set_RecMode
* @brief RS_485接口设置为接收模式
* @param None
* @retval None
*/
static void RS485_Set_RecMode()
{
}
/*
* @name putchar
* @brief 字符发送函数重定向
* @param c:发送的字符
* @retval char
*/
extern char putchar(char ch)
{
UART1.UART_SendData((uint8_t)ch); //在putchar函数内直接调用串口发送字符函数
return ch;
}
/*
* @name UART1_isr
* @brief 串口1中断处理函数
* @param None
* @retval None
*/
void UART1_isr() interrupt 4
{
if(RI)
{
RI = (bit)0; //清除接收中断标志
/*UART1_Rec_LENGTH宏定义为10,所以接收的数据不能超过10个字节
UART1.ucRec_Cnt表示数组下标,初始化为0*/
if(UART1.ucRec_Cnt < UART1_Rec_LENGTH)
{
ucRec_Buffer[UART1.ucRec_Cnt++] = SBUF;
}
UART1.ucRec_Flag = TRUE; //接收完成标志位
}
if(TI)
{
TI = (bit)0; //清除发送中断标志
UART1.ucTX_Busy_Flag = FALSE; //清除忙碌标志
}
}
/*
* @name Protocol
* @brief 串口协议
* @param None
* @retval None
*/
static void Protocol()
{
}
/********************************************************
End Of File
********************************************************/
看教学时需要注意的地方
1.在串口协议中,奇偶校验位用的不多,一般用CRC校验,在Modbus总线中会有介绍
2.收发双方的比特率要一致
3.串口通信时,STC-ISP软件尽量选择外部时钟,因为内部时钟受环境因素影响比较大,会导致波特率发生偏差,会有不稳定现象
4.发送数组的函数中形参指针不能++,所以改为+i;如果将形参赋给另一个指针,那指针就可++
5.printf打印重定向:首先main.h中添加头文件< stdio.h >,然后Usart1.c中修改putchar函数,内部调用串口发送字符函数,然后main.c主函数中使用printf(“xxx:%d”,xxx)通过串口打印数据