题外话:
说真的,我的32 的专栏确实停了好久,其实草稿箱里面又几篇32的文章,只是由于内容不完整就迟迟没有发布。具体看到有一个小伙伴需要一篇32关于串口的文章,所以,我也来学习一下吧~~~
正篇:
通信在嵌入式系统很常见,嵌入式微处理器的通信接口类型有很多,比如UART,SPI,IIC,CAN等,其中UART(通用异步收发器)是运用最广泛的。在stm32的内部配备了功能更强大的USART(通用同步/异步收发器),这个接口有具备了UART的功能。所以这篇文章就着重讲USART怎么用。
UART(通用异步收发器)
USART(通用同步/异步收发器)
看见他们名名字的区别,我们就想想什么是同步,什么是异步。
同步:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
串行通信:就是用一根数据线或者很少量的数据线将数据一位一位的按顺序依次传送。
并行通信:就是用多条数据线同时传送多位数据,可以这样理解:你的数据有多少位,就用多少根数据线来传,一比特位就用一根线。
所以!这样一比较,就可以知道:并行通信比串行通信速度快,但是用线比串行通信多,并且 并行通信的数据线都不能太长。
对于stm32,我们叫串口,那肯定就是用的串行通信方式,至于是同步还是异步,具体看自己需要,按照你自己的需要,到时候在配置串口的时候就选则是同步还是异步。
注意:由于32有两种串行通信接口:USART 和UART。这两个就是一个同步/异步,另一个就是同步。上面有关于同步与异步的概念的介绍,其实在32中,说简单点,同步就是有一根儿时钟信号线,用于数据的同步。
所以,你如果想用同步的方式,就用USART;如果想用异步的方式,那就UART。
(不用问我可不可以用USART当作UART使用?理论上是可以的,不是说了嘛,USART其实也具备UART的功能,但是谁闲着没事明明有UART却想吧USART改成UART使用呢?(是的,我无聊的想过,但是由于没有找到这一块的参考资料,加上觉得这样就是在浪费时间,于是就此作罢))
/* 配置串口 */
USART_InitStructure.USART_BaudRate=115200; //波特率了设置为115200
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用硬件流控制
USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx; //使能接收和发送
USART_InitStructure.USART_Parity=USART_Parity_No; //不使用奇偶校验位
USART_InitStructure.USART_StopBits=USART_StopBits_1; //1位停止位
USART_InitStructure.USART_WordLength=USART_WordLength_8b; //字长设置为8位
USART_Init(USART1, &USART_InitStructure); //初始化串口
波特率:表示数据的通信速率,波特率是指每秒钟传输的二进制位数,单位为比特每秒(bit/s),波特率越大,数据传输速度越快。
硬件流控制:这个概念就看这个博主的文章吧,我们一般不使用硬件流控制。
接收和发送:这里就需要有单工,半双工,全双工的概念了。使能接收和发送就是全双工。
奇偶验位,停止位,数据字长:这个就是关于串行通信协议里面的数据帧格式,一般就这样设置。
这个实验没有用到串口的中断哈!!串口之所以能打印,是因为输出重定向。
这样串口就可以打印出信息,一般多用于调试。
这个实验我用到了定时器中断,定时器用来定时一秒,然后串口就打印出定时的秒数。
串口的源文件:就是很基础的配置。
#include "myusart.h"
void MyUsart_init(void)
{
GPIO_InitTypeDef MyUsart_GPIO_InitStruct ;
USART_InitTypeDef MyUSART_InitStruct;
// 这两个结构体的定义一定要放在时钟的之前,否者编译器警告
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA,ENABLE);
// usart1 Tx PA9
MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP ;
MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
MyUsart_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);
//usart1 Rx PA10
MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);
// 初始化串口1
MyUSART_InitStruct.USART_BaudRate = 115200 ;// 波特率
MyUSART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
MyUSART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 收和发模式
MyUSART_InitStruct.USART_Parity = USART_Parity_No;// 无奇偶校验位
MyUSART_InitStruct.USART_StopBits = USART_StopBits_1;// 一位停止位
MyUSART_InitStruct.USART_WordLength = USART_WordLength_8b;// 字长为8位的数据模式
USART_Init(USART1, &MyUSART_InitStruct);
USART_Cmd(USART1, ENABLE);// 使能串口1
}
// 发送一个字符
void Usart_send_byte(USART_TypeDef* USARTx,uint16_t Data)
{
USART_SendData(USARTx,Data);
while( USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET );
}
// 发送字符串,遇到字符串结尾标志‘\0’结束
void Usart_send_string(USART_TypeDef* USARTx,char *arr)
{
uint16_t i = 0;
do
{
Usart_send_byte(USARTx,*(arr + i));
i++;
}while(*(arr + i) != '\0');
while( USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET );
}
//重定向 printf
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return (ch);
}
//重定向 输入
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
定时器.c
#include "tim.h"
// 用的是通用定时器2
void TIM_init(uint32_t ARR,uint32_t psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//设中断组
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1);
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
// 定时器2初始化
TIM_TimeBaseInitStruct.TIM_Period = ARR;
TIM_TimeBaseInitStruct.TIM_Prescaler = psc;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStruct);
// 使能定时器2
TIM_Cmd(TIM2, ENABLE);
// 清除中断,避免系统启动中断会立即产生中断、
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 定时器中断更新
TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE);
// 定时器2 的中断初始化
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(& NVIC_InitStruct);
}
主函数
#include "stm32f10x.h" // Device header
#include "tim.h"
#include "myusart.h"
#include "ledtest.h"
uint8_t cnt =0;
int main (void)
{
LED_GPIO_init();//led只是用来测试的
TIM_init( 1999, 35999);
MyUsart_init();
while(1)
{
}
}
void TIM2_IRQHandler(void)
{
static uint8_t time_cnt = 0;
time_cnt +=1;
if( TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET )
{
printf("time:%d\n",time_cnt);
/* led测试代码
if(cnt == 0)
{
cnt =1;
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}
else if(cnt == 1)
{
cnt =0;
GPIO_SetBits(GPIOB,GPIO_Pin_1);
}
*/
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
这里的 “ledtest.h” 可以不用管,写这个是用来测试我的代码的,现在是测试好了的。
成功的实验现象就是:板子一复位,串口就开始打印出定时秒数:1,2,3,4,5…
通过往串口发送一个字符来控制led灯的亮灭。
串口.c
#include "myusart.h"
#include
void LED_GPIO_init(void)
{
GPIO_InitTypeDef LEDinit;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
LEDinit.GPIO_Mode = GPIO_Mode_Out_PP ;
LEDinit.GPIO_Pin = GPIO_Pin_1;
LEDinit.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB,&LEDinit);
}
//重定向 printf
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return (ch);
}
//重定向 输入
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置系统中断优先级分组4 ,串口1中断
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
// 发送一个字符
void Usart_send_byte(USART_TypeDef* USARTx,uint16_t Data)
{
USART_SendData(USARTx,Data);
while( USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET );
}
// 发送字符串,遇到字符串结尾标志‘\0’结束
void Usart_send_string(USART_TypeDef* USARTx,char *arr)
{
uint16_t i = 0;
do
{
Usart_send_byte(USARTx,*(arr + i));
i++;
}while(*(arr + i) != '\0');
while( USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET );
}
void USART1_IRQHandler(void)
{
char temp;
if( USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
{
temp = USART_ReceiveData(USART1);
if (temp == 'o')
{
GPIO_ResetBits( GPIOB, GPIO_Pin_1);
Usart_send_string(USART1,"led is open\n");
}
if (temp == 'c')
{
GPIO_SetBits( GPIOB, GPIO_Pin_1);
Usart_send_string(USART1,"led is close\n");
}
}
}
main.c
#include "stm32f10x.h" // Device header
#include "myusart.h"
int main (void)
{
uart_init(115200);
LED_GPIO_init();
while(1)
{
}
}
现象:我往串口助手发送一个’o’,led亮。‘c’,led灭。
对于这个中断实验,其实要明白判断串口的中断标志位,然后进行相应的功能。