C8T6超绝模块--USART串口通信
C8T6超绝模块–USART串口通信
注意USART的引脚请自行查阅相应的数据手册
本模块主要实现功能
实现开发板与电脑通信,在开发板上电时通过 USART 发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑
大纲
- USART原理和结构体讲解
- 代码流程(收发数据)
- 代码流程(实现控制)
具体案例
USART原理和结构体讲解
在USART–串口通信一文中有详细解释,这里就不做详细解释
代码流程(收发数据)
USART.H
这是使用的宏定义
#ifndef __BSP_USART_H
#define __BSP_USART_H#include "stm32f10x.h"
#include <stdio.h>/** * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线* 2-修改GPIO的宏*/#define DEBUG_USART1 1
#define DEBUG_USART2 0
#define DEBUG_USART3 0
#define DEBUG_USART4 0
#define DEBUG_USART5 0#if DEBUG_USART1
// 串口1-USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler#elif DEBUG_USART2
// 串口2-USART2
#define DEBUG_USARTx USART2
#define DEBUG_USART_CLK RCC_APB1Periph_USART2
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_2
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_3#define DEBUG_USART_IRQ USART2_IRQn
#define DEBUG_USART_IRQHandler USART2_IRQHandler#elif DEBUG_USART3
// 串口3-USART3
#define DEBUG_USARTx USART3
#define DEBUG_USART_CLK RCC_APB1Periph_USART3
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOB)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOB
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_RX_GPIO_PORT GPIOB
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11#define DEBUG_USART_IRQ USART3_IRQn
#define DEBUG_USART_IRQHandler USART3_IRQHandler#elif DEBUG_USART4
// 串口4-UART4
#define DEBUG_USARTx UART4
#define DEBUG_USART_CLK RCC_APB1Periph_UART4
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOC
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_RX_GPIO_PORT GPIOC
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_11#define DEBUG_USART_IRQ UART4_IRQn
#define DEBUG_USART_IRQHandler UART4_IRQHandler#elif DEBUG_USART5
// 串口5-UART5
#define DEBUG_USARTx UART5
#define DEBUG_USART_CLK RCC_APB1Periph_UART5
#define DEBUG_USART_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOC
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_12
#define DEBUG_USART_RX_GPIO_PORT GPIOD
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_2#define DEBUG_USART_IRQ UART5_IRQn
#define DEBUG_USART_IRQHandler UART5_IRQHandler#endifvoid USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data);
void Usart_SendHalfWord(USART_TypeDef * pUSARTx,uint16_t data);
void Usart_SendArray(USART_TypeDef * pUSARTx,uint8_t *array,uint8_t num);
void Usart_SendStr(USART_TypeDef * pUSARTx,uint8_t *str);#endif
USART.C
首先设置中断的优先级
static void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitSructure;// 进行嵌套向量中断控制器的选择NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 配置USART为中断源NVIC_InitSructure.NVIC_IRQChannel = DEBUG_USART_IRQ;// 抢断优先级NVIC_InitSructure.NVIC_IRQChannelPreemptionPriority = 1;// 子优先级NVIC_InitSructure.NVIC_IRQChannelSubPriority = 1;// 使能中断NVIC_InitSructure.NVIC_IRQChannelCmd = ENABLE;// 初始化配置NVICNVIC_Init(& NVIC_InitSructure);
}
其次是USART串口的初始化
void USART_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打开串口GPIO的时钟DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);// 打开串口外设的时钟DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE);// 初始化GPIO, 将USART Tx配置为推挽复用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure);// 将USART Rx的GPIO配置为浮空输入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);// 配置串口的工作的参数// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置字数据长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(DEBUG_USARTx,& USART_InitStructure);// 串口中断优先级配置NVIC_Configuration();// 使能串口接收中断USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);// 使能串口(其实是打开UE)USART_Cmd(DEBUG_USARTx,ENABLE);}
注意初始化串口时,分别初始化串口的TX(推挽复用)RX(浮空输入)两个引脚后,再配置串口的结构体的参数。再调用前面的设置中断的优先级。使能串口接收中断
上面完成的是串口初始化的代码,下面是具体实现数据发送的功能函数
// 发送一个字节
void Usart_SendByte(USART_TypeDef* pUSARTx,uint8_t data)
{USART_SendData(pUSARTx,data);while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);}// 发射两个字节
void Usart_SendHalfWord(USART_TypeDef * pUSARTx,uint16_t data)
{uint8_t temp_h,temp_l;temp_h = (data & 0xff00) >> 8;temp_l = data & 0xff;USART_SendData(pUSARTx,temp_h);while( USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET );USART_SendData(pUSARTx,temp_l);while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE));}// 发送8位数据的数组
void Usart_SendArray(USART_TypeDef * pUSARTx,uint8_t *array,uint8_t num)
{uint8_t i;for(i = 0; i < num;i++){ Usart_SendByte(pUSARTx,*array++);}while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}// 发送字符串
void Usart_SendStr(USART_TypeDef * pUSARTx,uint8_t *str)
{uint8_t i = 0;do{Usart_SendByte(pUSARTx,*(str + i));i++;}while(*(str + i) != '\0');while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);}// 重定向c库函数printf到串口,重定向后可使用printf函数int fputc(int ch,FILE *f)
{// 发送一个字节到串口USART_SendData(DEBUG_USARTx,(uint8_t)ch);// 等待发送完成while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TXE) == RESET);return (ch);
}// 重定向c库函数scanf到串口,重写向后可使用的scanf,getchar函数
int fgetc(FILE *f)
{// 等待串口输入数据while (USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx);
}
USART中断服务函数
// 串口中断函数
void DEBUG_USART_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE) != RESET){ucTemp = USART_ReceiveData(DEBUG_USARTx);USART_SendData(DEBUG_USARTx,ucTemp);}}
这是串口完成接收时,接收到数据后,存储在ucTemp然后再发送回来他接收到的数据
main
# include "stm32f10x.h"#include "bsp_led.h"
#include "bsp_usart.h"void Delay( uint32_t count)
{for(;count != 0;count--);
}int main(void)
{USART_Config(); printf("串口printf函数测试\n");
putchar('p'); while(1){}}
其实这里主要还是就是完成串口的初始化
代码流程(实现控制)
主要的串口初始化和接收函数和上面一样
我们这里主要通过接收到的数据控制小灯
小灯初始化请见:C8T6超绝模块–点亮小灯
而与上面的差别,主要体现在主函数里面
main
# include "stm32f10x.h"#include "bsp_led.h"
#include "bsp_usart.h"int main(void)
{USART_Config();uint8_t ch;LED_GPIO_Config();while(1){printf("这是串口控制LED的程序\n");ch = getchar();printf("ch=%c\n",ch);switch(ch){case '1':LED_G(ON);break;case '2':LED_G(OFF);break;default:LED_G(OFF);}}}
这里主要通过getchar()来把USART返回的值存储到ch中,然后再通过ch的值进行判断,来进行点亮或者熄灭小灯
但是这里要注意点一点是:
我们使用这个getchar()要实现返回接收到的串口数据值,需要实现他的重定向
在上面的代码主要体现在这一块
// 重定向c库函数scanf到串口,重写向后可使用的scanf,getchar函数
int fgetc(FILE *f)
{// 等待串口输入数据while (USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx);
}
判断是否接受完成,如果接收完成,就返回接收到的值