一.仅向上位机打印调试信息
单纯利用串口向上位机打印调试信息,程序如下:
void USART1_Init( uint32_t btl )
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStruct );
USART_InitStruct.USART_BaudRate = btl;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init( USART1, &USART_InitStruct );
USART_Cmd( USART1, ENABLE );
}
//串口重定向,直接利用printf函数输出调试信息
int fputc( int ch, FILE *f )
{
USART_SendData( USART1, ( uint8_t )ch );
while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );
return ch;
}
记得包含头文件,勾选Use MicroLIB,以使用printf等函数
二.与上位机交互信息
相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。
上位机向单片机发送字符串,接收后再发送给上位机:
void USART1_Init( uint32_t btl )
{
/* 结构体声明 */
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
/* 打开时钟 */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
/* GPIO配置 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStruct );
/* 串口配置 */
USART_InitStruct.USART_BaudRate = btl;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init( USART1, &USART_InitStruct );
/* 中断配置 */
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断
/* 使能串口 */
USART_Cmd( USART1, ENABLE );
}
volatile uint8_t n=0;
//接收缓冲区
uint8_t USART1_Rx_Buf[100];
void USART1_IRQHandler( void )
{
if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )
{
USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );
n++;
}
USART_ClearFlag( USART1, USART_IT_RXNE );
}
每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。
三.作为驱动接口
一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。
usart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。
usart3:
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
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 USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
time7:
extern vu16 USART3_RX_STA;
//定时器7中断服务程序
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断
{
USART3_RX_STA|=1<<15; //标记接收完成
TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志
TIM_Cmd(TIM7, DISABLE); //关闭TIM7
}
}
//通用定时器7中断初始化
//这里时钟选择为APB1的2倍,而APB1为42M
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//通用定时器中断初始化
//这里始终选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
void TIM7_Int_Init(u16 arr,u16 psc)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能
//定时器TIM7初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
TIM_Cmd(TIM7,ENABLE);//开启定时器7
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
四.结合DMA接收数据帧
当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。
这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。
使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。
程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。
代码:
uint8_t USART2_Rx_Buf[12];
void USART2_Config( void )
{
/* 声明各结构体 */
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
DMA_InitTypeDef DMA_InitStruct;
/* 打开时钟 */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
/* GPIO配置 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //USART2_Tx:PA2
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct );
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //USART2_Rx:PA3
GPIO_Init( GPIOA, &GPIO_InitStruct );
/* 串口配置 */
USART_DeInit( USART2 );
USART_InitStruct.USART_BaudRate = 9600; //波特率:9600
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init( USART2, &USART_InitStruct);
/* 中断配置 */
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init( &NVIC_InitStruct );
USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //空闲中断
USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //开启DMA接收
/* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/
DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04; // 设置DMA源地址:串口数据寄存器地址*/
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf; // 内存地址(要传输的变量的指针)
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:从外设到内存
DMA_InitStruct.DMA_BufferSize = 12; // 传输大小 一帧数据12字节
上一篇:STM32—PID控制在直流电机中的应用
下一篇:STM32—TIMx实现编码器四倍频
推荐阅读最新更新时间:2024-11-12 09:29
- 热门资源推荐
- 热门放大器推荐
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 LTC3615EFE-1 的参考设计
- 基于 STBB3J 的 STEVAL-ISA141V1、2A、2 MHz、3.3 Vout、高效、双模式降压-升压 DC-DC 转换器评估板
- 使用 3-LED 驱动的 MCP1643 升压转换器 LED 驱动解决方案
- 用于增加 LED 电流的 AL5802 可调电流吸收线性 LED 驱动器的典型应用
- 使用 Microchip Technology 的 TC18C46MJE 的参考设计
- AM2G-1209DZ ±9V 2 瓦 DC-DC 转换器的典型应用
- L7815C 可调输出稳压器的典型应用(7 至 30 V)
- WRL-15435,SparkFun Thing Plus - XBee3 Micro (U.FL)
- ADA4858-3ACPZ-RL 直流耦合、单电源运算放大器电路的典型应用电路
- 使用 Analog Devices 的 LT124XCN8 的参考设计