硬件平台:stm32f10xZET6
开发环境:keil MDK uVision v4.10
开发语言:C、ST_lib_3.5固件库
【串口通信】
typedef struct
{
u32 USART_BaudRate;
u16 USART_WordLength;
u16 USART_StopBits;
u16 USART_Parity;
u16 USART_Mode;
u16 USART_HardwareFlowControl;
} USART_InitTypeDef;
typedef struct
{
u16 USART_Clock;
u16 USART_CPOL;
u16 USART_CPHA;
u16 USART_LastBit;
} USART_ClockInitTypeDef;
串口外设主要由三个部分组成,分别是:波特率的控制部分、收发控制部分及数据存储转移部分。
CR1、 CR2、 CR3、SR,即 USART 的三个控制寄存器(Control Register)及一个状态寄存器(Status Register)
当我们需要发送数据时,内核或 DMA 外设把数据从内存(变量)写入到发送数据寄存器 TDR 后,发送控制器将适时地自动把数据从 TDR 加载到发送移位寄存器,然后通过串口线Tx,把数据一位一位地发送出去,在数据从 TDR 转移到移位寄存器时,会产生发送寄存器TDR 已空事件 TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件 TC,这些事件可以在状态寄存器中查询到。
而接收数据则是一个逆过程,数据从串口线 Rx 一位一位地输入到接收移位寄存器,然后自动地转移到接收数据寄存器 RDR,最后用内核指令或 DMA读取到内存(变量)中。
调用了库函数 RCC_APB2PeriphClockCmd()初始化了USART1 和 GPIOA 的时钟,这是因为使用了 GPIOA 的 PA9 和 PA10 的默认复用USART1 的功能,在使用复用功能的时候, 要开启相应的功能时钟 USART1。
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
在使用外设时,不仅要使能其时钟,还要调用此函数使能外设才可以正常使用。
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* 使能串口1接收中断 */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
USART重要库函数:
USART_SendData()
USART_ReceiveData()
USART_GetFlagStatus()
USART_Init()
USART_ITConfig()
USART_Cmd()
【USART_GetFlagStatus()】
USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
USART_FLAG_LBD: LIN Break detection flag
USART_FLAG_TXE: Transmit data register empty flag
USART_FLAG_TC: Transmission Complete flag
USART_FLAG_RXNE: Receive data register not empty flag
USART_FLAG_IDLE: Idle Line detection flag
USART_FLAG_ORE: OverRun Error flag
USART_FLAG_NE: Noise Error flag
USART_FLAG_FE: Framing Error flag
USART_FLAG_PE: Parity Error flag
【USART_ITConfig()】
USART_IT_CTS: CTS change interrupt (not available for UART4 and UART5)
USART_IT_LBD: LIN Break detection interrupt
USART_IT_TXE: Transmit Data Register empty interrupt
USART_IT_TC: Transmission complete interrupt
USART_IT_RXNE: Receive Data register not empty interrupt
USART_IT_IDLE: Idle line detection interrupt
USART_IT_PE: Parity Error interrupt
USART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
串口的发送中断有两个,分别是:
>>发送数据寄存器空中断(TXE)
>>发送完成中断(TC)
一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。
keil的虚拟串口Debug调试:
需要设置为 Use Simulator模式
开启 View-command window输入命令
MODE COM1 115200,0,8,1
ASSIGN COM1
技巧:【Debug】选项卡下左侧 Initialization File 中点击【...】 新增一个默认调试命令的.ini文件,如debug.ini
虚拟串口软件 VSPD 开启两个COM,使用secureCRT连接另外一个COM口,查看接收情况
第一个AT指令是“ATE0Q0V1”,很是迷惑了一阵,后来才明白这是三个指令的合并:”ATE0+ATQ0+ATV1“。
ATE0:不回显字符。
ATE1:回显字符。
ATQ0: 返回结果码。
ATQ1:不返回结果吗。
ATV0:返回数字码。
ATV1: 返回文字码。
记录下接线颜色对应管脚
>>usart1连USB-TTL
重定向printf到usart1
往usart1发AT
终端测试
>>usart1连GPRS,usart2连USB-TTL
往usart1发AT
逐个字符收数据到公共缓冲区
使用usart2重定向的printf输出缓冲区内容
终端测试
// 中断处理函数需:时钟配置、中断配置、USART中的接收中断使能、中断处理函数
void USART1_IRQHandler(void)
{
uint8_t ch;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
//ch = USART1->DR;
ch = USART_ReceiveData(USART1);
printf( "%c", ch ); //将接受到的数据直接返回打印
}
}
-----------------------------------------------------------
char buf[70] = {0}; // 全局缓冲区,extern调用
void USART1_IRQHandler(void)
{
uint8_t ch;
int index = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
//ch = USART1->DR;
buf[index] = USART_ReceiveData(USART1);
printf( "%c", buf[index]); //将接受到的数据通过usart1的printf打印显示
index++;
}
}
// usart.c
#include "usart.h"
extern sdstring atcmd_recv_buff;
/**
* @ USART1 GPIO 配置,工作模式配置。115200 8-N-1
*/
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 配置 USART1 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 GPIO 配置 */
/* 配置 USART1 Tx (PA9) 为复用推挽输出模式,速度50MHZ */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置 USART1 Rx (PA10) 为浮空输入模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART1 工作参数配置:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure); // 写入初始化信息
USART_Cmd(USART1, ENABLE); /* 使能 USART1 */
}
/**
* @ USART1 NVIC 中断配置
*/
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置 NVIC 优先组0 (4位的16级优先级可选 - 0~15) */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* 使能USART1中断 */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 1~15
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// usart1中断处理函数在 stm32f10x_it.c line:156
/**
* @ USART2 GPIO 配置,工作模式配置。115200 8-N-1
*/
void USART2_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
sds_clean(&atcmd_recv_buff); // 接收前初始化清理一次公共缓冲区
/* 使能 USART2 时钟 */
RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
/* 配置 USART2 Tx (PA2) 为复用推挽输出模式,速度50MHz */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置 USART2 Rx (PA3) 为输入浮空模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART2 工作参数配置:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure); // 写入初始化信息
/* 使能串口2接收中断 */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE); /* 使能 USART2 */
}
/* USART1 - 发送字符、接收字符 (轮询也用于printf重定向) */
int SendChar (int ch) { /* 发送字符 8bit/byte */
USART_SendData(USART1, (unsigned char) ch); /* 将1字节数据发往串口寄存器 */
// while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); /* 轮询等待传输结束 */
while (!(USART1->SR & USART_FLAG_TXE));
return ch;
}
int GetData (void) { /* 读取字符 8bit/byte */
while (!(USART1->SR & USART_FLAG_RXNE));
return (USART_ReceiveData(USART1));
}
/* USART2 - 发送字符、接收字符 (轮询也用于printf重定向) */
int SendCharSerail2 (int ch) { /* 发送字符 8bit/byte */
USART_SendData(USART2, (unsigned char) ch);
while (!(USART2->SR & USART_FLAG_TXE));
return (ch);
}
int GetDataSerial2 (void) { /* 读取字符 8bit/byte */
while (!(USART2->SR & USART_FLAG_RXNE));
return (USART_ReceiveData(USART2));
}
/*********************************************END OF FILE**********************/
// retarget.c
#include
#include
#include "usart.h"
/*
* 函数名: fputc
* 描述 : 重定向 c 库函数 printf 到 USART1
*/
int fputc(int ch, FILE *f)
{
#if 1 // 1-USART1 : 0-USART2
USART_SendData(USART1, (unsigned char) ch); // 将1字节数据发往串口寄存器,printf测试usart2回显
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 轮询是否发送完成,标志位TC
#else
USART_SendData(USART2, (unsigned char) ch); // 将1字节数据发往串口寄存器
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); // 轮询是否发送完成,标志位TC
#endif
return (ch);
}
// 从USART中的数据
int fgetc(FILE *f)
{
#if 1
return (SendChar (GetData ()));
#else
return (SendCharSerail2 (GetData ()));
#endif
}
// at_cmd.c
#include "at_cmd.h"
sdstring atcmd_recv_buff; //atcmd_recv_buff.buff 全局缓冲区,存放返回数据
// 逐个发送AT命令集
void send_all_at_cmd (void)
{
char *p_res = NULL;
char *p_str = "hello";
send_at_cmd (AT_AT);
p_res = receive_data ();
print_log_info ("recv data");
// if "OK"/0
if (! strstr (p_res, "OK")) {
return ;
}
send_at_cmd (AT_CGATT);
p_res = receive_data ();
print_log_info ("recv data");
// if "OK"/0
send_at_cmd (AT_CGATT_1);
p_res = receive_data ();
print_log_info ("recv data");
// if "OK"/0
send_at_cmd (AT_CON_TCP);
p_res = receive_data ();
print_log_info ("recv data");
// if "CONNECT OK"
send_at_cmd (AT_CIPSEND);
p_res = receive_data ();
print_log_info ("recv data");
// if ">"
send_at_cmd (p_str);
p_res = receive_data ();
print_log_info ("recv data");
// if "HTTP/1.1 400 Bad Request"
#if 0
send_at_cmd (AT_CIPCLOSE); // 关闭TCP连接命令
p_res = receive_data ();
print_log_info ("recv cmd");
// if "OK"/0
#endif
}
// 发送单个AT命令,打印提示日志
void send_at_cmd (char *p_cmd)
{
//int i = 0;
#if 1
send_USART2 (p_cmd, strlen (p_cmd));
send_USART2 (AT_CONFIRM, strlen (AT_CONFIRM)); // \r\n
strcpy (atcmd_recv_buff.buff, p_cmd);
print_log_info ("send cmd");
#else
// 发送什么指令-回显到屏幕(USART1重定向了printf)单向传输测试
printf ("\r\n%s", p_cmd);
#endif
}
// 接收发送命令后GPRS通过串口端自动回复的数据
char* receive_data (void)
{
int i;
memset (atcmd_recv_buff.buff, 0, sizeof (atcmd_recv_buff.buff));
#if 1
// 轮询读取USART2接收寄存器的信息到atcmd_recv_buff.buff[]
do {
while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
atcmd_recv_buff.buff[i] = USART2->DR;
// 如果在USART2的接收寄存器中接收到 \n 字符,作为结束标志
if(atcmd_recv_buff.buff[i] == '\n') {
break;
}
i++;
} while(1);
return (atcmd_recv_buff.buff);
#else // used to test... 测试返回数据
memset (atcmd_recv_buff.buff, 0, sizeof (atcmd_recv_buff.buff));
strcpy (atcmd_recv_buff.buff, "OK\r\n");
return (atcmd_recv_buff.buff);
#endif
}
// 往连接GPRS串口的USART2串口中发送字符串数据
void send_USART2 (char *pdata, int len)
{
int index = 0;
for(index = 0; index < len; index++)
{
SendCharSerail2(pdata[index]);
}
}
/*
// 测试IP:123.58.34.245 测试端口:80
void send_connect_cmd (char *ip,int port)
{
char buf[40]={0};
send_at_cmd (AT_CIPSTART);
snprintf(buf, sizeof(buf), "\"%s\",%d", ip, port);
send_at_cmd (buf);
}
*/
// 打印输出全局缓冲区内容,即日志,通过USART1重定向了printf后显示到串口终端
void print_log_info (char *p_str)
{
printf ("\r\n%s:%s", p_str, atcmd_recv_buff.buff);
}
// 字符串清理
void sds_clean(sdstring *psds)
{
memset(psds->buff, 0, sizeof(psds->buff));
psds->pos=0;
}
// 字符串长度
int sds_length(sdstring *psds)
{
return psds->pos;
}
// 字符串追加
int sds_append_char(sdstring *psds, char ch)
{
if(psds->pos >= sizeof(psds->buff)-1)
{
return -1;
}
psds->buff[psds->pos]=ch;
psds->pos++;
return 0;
}
// at_cmd.h
#pragma once
#include
#include
#include "usart.h"
#include "setup.h"
/*--------------发送-----------------*/
// at命令头
#define AT_CMD_PREFIX "AT+"
#define CHAR_PLUS '+'
#define CHAR_CR '\r'
#define CHAR_POWER '^'
#define AT_CONFIRM "\r\n"
#define AT_FORMAT "ATE0Q0V1" // ATE1/0 回显/不回显字符; ATQ1/0 返回/不返回结果码; ATV1/0 返回/不返回结果码
// ATV1/0 决定了收到GPRS模块回复的数据是 <字符串> OK 或 <数字> 0
#define AT_AT "AT" // AT命令测试,返回OK
#define AT_ATI "ATI" // 查询固件版本信息
#define AT_CCID "AT+CCID" // 查询SIM ,CCID用于判断是否插卡
#define AT_CREG "AT+CREG?" // 查询网络注册情况
#define AT_CGATT "AT+CGATT=1" // 1.附着网络,返回OK
#define AT_CGATT_1 "AT+CGACT=1,1" // 2.激活PDP,返回OK,即可正常上网
#define AT_CIPSTART "AT+CIPSTART=\"TCP\"," // 连接服务端
#define AT_CON_TCP "AT+CIPSTART=\"TCP\",\"123.58.34.245\",80" // 连接服务端,IP和PORT目前为测试取值
#define AT_CON_IP "123.58.34.245"
#define AT_CON_PORT 80
#define AT_CIPSEND "AT+CIPSEND=20" // 返回>,就可以输入要发送的内容20表示有20个字节
#define AT_TMP_STRING "1234567890123456789" // 19个字符有效字符
#define AT_CIPCLOSE "AT+CIPCLOSE=1" // 关闭连接
#define AT_CIPSHUT "AT+CIPSHUT" // 关闭 GPRS/CSD PDP 场景
#define AT_CIPTMODE_START "AT+CIPTMODE=1" // 启动透传模式
#define AT_CIPTMODE_QUIT "AT+CIPTMODE=0" // 停止透传模式
#define PARSE_CMD_OK 0
#define PARSE_CMD_ERR -1
#define PARSE_CMD_NEEDMORE 1
typedef struct _sdstring {
int pos;
char buff[128];
} sdstring;
// 逐个发送AT命令集
void send_all_at_cmd (void);
// 发送单个AT命令,打印提示日志
void send_at_cmd (char * p_cmd);
// 接收发送命令后GPRS通过串口端自动回复的数据
char* receive_data (void);
// 往连接GPRS串口的USART2串口中按字节(8bit)逐个发送数据
void send_USART2 (char *pdata, int len);
// 打印输出全局缓冲区,即每次数据收发的日志信息
void print_log_info (char *p_str);
// void send_connect_cmd (char *ip,int port);
// 字符串处理
void sds_clean(sdstring *psds);
int sds_append_char(sdstring *psds, char ch);
int sds_length(sdstring *psds);
// 自定义的格式化输出
// void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...)
// setup.c
#include "Setup.h"
static __IO u32 TimingDelay;
/**
* @brief 启动系统滴答定时器 SysTick
*/
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
// if (SysTick_Config(SystemFrequency / 100000)) // ST3.0 库版本
if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5 库版本
{
/* Capture error */
while (1);
}
// 关闭滴答定时器
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
/**
* @brief us延时程序,10us为一个单位
* @arg nTime Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
*/
void Delay_us(__IO u32 nTime)
{
TimingDelay = nTime;
// 使能滴答定时器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != 0);
}
/**
* @brief 获取节拍程序
* @attention 在 SysTick 中断函数 SysTick_Handler()调用
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
/*********************************************END OF FILE**********************/
// led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define ON 0
#define OFF 1
/* 带参宏,可以像内联函数一样使用 */
#define LED1(a) if (a) \
GPIO_SetBits(GPIOB,GPIO_Pin_5); \
else \
GPIO_ResetBits(GPIOB,GPIO_Pin_5)
#define LED2(a) if (a) \
GPIO_SetBits(GPIOE,GPIO_Pin_5); \
else \
GPIO_ResetBits(GPIOE,GPIO_Pin_5)
void LED_Config(void);
#endif // __LED_H
// led.c
#include "led.h"
/*
* @ LED 初始化I/O控制
*/
void LED_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启GPIOB的外设时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*选择要控制的GPIOB引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
/*调用库函数,初始化GPIOB5*/
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*选择要控制的GPIOE引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
/*调用库函数,初始化GPIOE5*/
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* 关闭led灯 */
GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOE, GPIO_Pin_5);
}
/*********************************************END OF FILE**********************/
上一篇:STM32之USART 232串口通信<一>
下一篇:STM32:I2C接口读写EEPROM(AT24C02)试验例程
推荐阅读最新更新时间:2024-03-16 15:37