STM32新建工程标准模板和串口

                               STM32新建工程标准模板

  1. 使用的库:STM32F10x_StdPeriph_Lib_V3.5.0.rar(STM32F103RBT6为例)
  2. 例如:STM32F103RBT6是128kb,则选择:medium类型。即:startup_stm32f10x_md.s。选型如下图:
  3. 新建工程目录文件夹以及Project档目录,如下图:

对于工程,放在MDK-ARM里面,命名可以为xxx_Project,涉及到的头文件都放在stdlib的inc里。如:stm32f10x.h,stm32f10x_conf.h。CMSIS目录文件见下图。

4.修改stm32f10x.h头文件:选择型号和选择标准库选项。主要就这两个地方。添加头文件目录。

5.编译并烧录程序,点亮一个LED灯,测试正常。对于GPIO初始化必须有GPIO_InitStructure.GPIO_Speed这个配置,否则LED不亮,之前倒没有留意过这个问题。


以下为程序方面记录:

1.调试查看系统时钟,特别对于无晶振系统很有用。

RCC_ClocksTypeDef RCC_Clocks;

RCC_GetClocksFreq(&RCC_Clocks);


2.系统倍频。

若无外部晶振,倍频到64M,用下面代码。需要先把system_stm32f10x.c文件里的代码:#define SYSCLK_FREQ_72MHz  72000000注释掉!若用带晶振的开发板测试64M,如果不把#define ..72M 这句注释,则会直接HardFault

void SystemClk_Init()//8/2*16=64MHz
{
/* Enable Prefetch Buffer */
		FLASH->ACR |= FLASH_ACR_PRFTBE;
		/* Flash 2 wait state */
		FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
		RCC_DeInit(); 
		RCC_HSICmd(ENABLE);
		while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
		RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
		RCC_HSEConfig(RCC_HSE_OFF);
		RCC_LSEConfig(RCC_LSE_OFF);
		RCC_PLLConfig(RCC_PLLSource_HSI_Div2,RCC_PLLMul_16); 
		RCC_PLLCmd(ENABLE);
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		while(RCC_GetSYSCLKSource() != 0x08);	
}

3.对于系统精确延时函数:

新建System文件夹,写delay.c和delay.h。

下面为delay.c函数:

#include "stm32f10x.h"
//////////////////////////////////////////////////////////////////////////////////	 
static double fac_us=0;								   						
static RCC_ClocksTypeDef     RCC_Clocks;	
void delay_init()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//HCLK/8->主频8分频=64/8=8MHz->1us:8
	RCC_GetClocksFreq(&RCC_Clocks);
	fac_us=RCC_Clocks.SYSCLK_Frequency/8000000;//fac_us=8;		 
}								    
    								   
void delay_us(unsigned int nus)
{		
	unsigned int ticks;
	unsigned int told,tnow,tcnt=0;
	unsigned int reload=SysTick->LOAD;					  	 
	ticks=nus*fac_us; 					 		 
	tcnt=0;				
	told=SysTick->VAL;        				
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;		
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;
		}  
	};								    
}

void delay_ms(unsigned short nms)
{		 						   
	delay_us((unsigned int)(nms*1000));	
}

下面为delay.h文件:

#ifndef __DELAY_H
#define __DELAY_H

void delay_init(void);
void delay_us(unsigned int nus);
void delay_ms(unsigned short nms);

#endif

主函数调用:注意顺序!!

void main()
{
    ...
	while(SysTick_Config(8000)!=0);//此处针对无晶振并64M分频8,即systick为8M
	delay_init();
    ...
}

补充:因为SysTick_Config()函数会设置系统定时器的时钟源,所以要先放前面。SysTick_Config()函数同样也会使能系统定时器中断。


4.GPIO电平翻转,对于LED适用。

GPIOC->ODR ^= GPIO_Pin_0;//V3.5固件库
LL_GPIO_TogglePin(GPIOC,LL_GPIO_PIN_0);//LL库

5.串口通信

一、普通串口方式

配置:需要注意点->开启时钟,开启中断,注册到NVIC,否则串口接收中断无效

void Uart1_Init()
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
  //USART1 Tx(PA.9) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  //USART1 Rx(PA.10) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  USART_InitTypeDef USART_InitStructure;
  USART_InitStructure.USART_BaudRate = 9600; 
  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_Tx | USART_Mode_Rx; 
  USART_Init(USART1, &USART_InitStructure);
  USART_Cmd(USART1, ENABLE); 
  
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 
}
void Init_NVIC()
{
  NVIC_InitTypeDef NVIC_InitStructure;   
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; 
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
  NVIC_Init(&NVIC_InitStructure);
}

a.发送封装函数:

void Uart1_SendData(unsigned char data)
{
  USART1->SR;
  USART_SendData(USART1,data);
  while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
void Uart1_SendString(char *data)//字符串
{
  while(*data)
  {
    Uart1_SendData(*(data++));
  }
}
void Uart1_SendLength(unsigned char*DP,unsigned int num)//指定长度字符
{
  for(int k=0;k<num;k++)
  {
    Uart1_SendData(DP[k]);
  }
}

b.接收中断函数:

void USART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
    RX_Buffer_Temp=USART_ReceiveData(USART1);
    if(uart_num<498)
    {
      RX_Buffer[uart_num] = RX_Buffer_Temp;
      uart_num++;
	  rec_flag=1;
    }
    else
    {
      uart_num=0;
    }   
  }
  if(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET) //  ORE 
  {
      USART_ClearFlag(USART1,USART_FLAG_ORE);
      RX_Buffer_Temp=USART_ReceiveData(USART1);
  }
}

二、DMA串口收发

1.配置差异:

void Uart1_Init()
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
  //USART1 Tx(PA.9) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  //USART1 Rx(PA.10) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  USART_InitTypeDef USART_InitStructure;
  USART_InitStructure.USART_BaudRate = 9600; 
  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_Tx | USART_Mode_Rx; 
  USART_Init(USART1, &USART_InitStructure);
  USART_Cmd(USART1, ENABLE); 
  
  //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 
  USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
  USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
}

串口DMA空闲中断接收数据

通用配置如下:

void Uart1_Init()
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
  //USART1 Tx(PA.9) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  //USART1 Rx(PA.10) 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  USART_InitTypeDef USART_InitStructure;
  USART_InitStructure.USART_BaudRate = 9600; 
  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_Tx | USART_Mode_Rx; 
  USART_Init(USART1, &USART_InitStructure);
  USART_Cmd(USART1, ENABLE); 
  
  //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 

  USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//如果写了这个就需要在空闲中断处理!
}
void Usart1_DMA_init()
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 
		/* 接收通道 */
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&USART_RX_BUF;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = sizeof(USART_RX_BUF);//此值很重要
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	    /* 发送通道 */
    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)&USART_TX_BUF;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize         = 0;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4,&DMA_InitStructure);

    DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);//传输完成
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
    
    DMA_Cmd(DMA1_Channel5, ENABLE); 
    DMA_Cmd(DMA1_Channel4, ENABLE); 
		
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
		
}  

A.串口DAM发送

很简单,直接上代码,关于配置通用。因为每次发送都要重新配置发送字节长度,所以配置的时候DMA_InitStructure.DMA_BufferSize         = 0;这个就无所谓了。但是接收不一样。见下面分析。

void Uart1_DMA_Send(unsigned char * buff, unsigned int len)//发送封装函数
{
    memcpy(USART_TX_BUF, buff, len);
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA1_Channel4->CMAR = (u32)USART_TX_BUF;
    DMA_SetCurrDataCounter(DMA1_Channel4, len);
    DMA_Cmd(DMA1_Channel4, ENABLE);
}

下面是DMA串口发送完成中断,也可以加一些处理函数,但目前没用到。

void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC4) == SET)//发送传输完成
    {
          DMA_ClearFlag(DMA1_IT_TC4);
          ...
    }
}

B.串口DMA接收

目前来看,DMA接收最有价值的就是空闲中断接收,这样可以判断不定长字节接收。先讨论下定长数据接收,这里有个接收完成中断其实可以很好的利用。见代码:

void DMA1_Channel5_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC5) == SET)//接收完成
    {
       DMA_ClearFlag(DMA1_IT_TC5);
       DMA_Cmd(DMA1_Channel5, DISABLE);
       DMA1_Channel5->CMAR  = (uint32_t)USART_RX_BUF;//存放的数组buffer
       DMA1_Channel5->CNDTR = 5; //这个值就是接收的定长值,自己定义
       DMA_Cmd(DMA1_Channel5, ENABLE);
       ...//这里可以加标志位,在主函数处理接收的数据
    }
}

注意:对于接收配置项DMA_InitStructure.DMA_BufferSize = 5;//sizeof(USART_RX_BUF);这个值要么配置的时候先规定好,要么在主函数的时候调用DMA1_Channel5->CNDTR = 5;重新写入,否则不正确的话肯定进不了完成中断。此完成中断只有达到接收的个数大于等于设置的值才会进入中断,但是不影响DMA接收。对于USART_ITConfig函数,如果ENABLE了,就需要写对应的中断函数,否则直接卡死。


下面写不定长DMA接收,利用空闲中断处理,需要USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 

代码如下:

void USART1_IRQHandler(void)
{
  unsigned char uart_info=0; 
  uart_info=uart_info;//不加的话keil5会警告,原因自行百度
  if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)//串口空闲中断
  {
    uart_info = USART1->SR;
    uart_info = USART1->DR;
    DMA_Cmd(DMA1_Channel5,DISABLE);
    dma_recv_num = 200 -  DMA_GetCurrDataCounter(DMA1_Channel5);//接收数据长度
    DMA1_Channel5->CNDTR=200;//最大缓存
    DMA_Cmd(DMA1_Channel5,ENABLE);
    dma_receive_flag = 1;//设定标志位
  }
}

测试DMA空闲串口中断接收到的数据再用DMA发到到外设。高强度100ms收发测试。

int main(void)
{
	MyGPIO_Init();
	SystemClk_Init();
	Uart1_Init();
	Usart1_DMA_init();
	while(SysTick_Config(8000)!=0);
	delay_init();
	//RCC_GetClocksFreq(&RCC_Clocks);
	Init_NVIC();
    while (1)
    {
		if(dma_receive_flag)
		{
			dma_receive_flag=0;
			memcpy(RT_Buffer, USART_RX_BUF, dma_recv_num);
			Uart1_DMA_Send(RT_Buffer,dma_recv_num);
		}
		
    }
}



void USART1_IRQHandler(void)
{
  unsigned char uart_info=0; 
  uart_info=uart_info;
  if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)
  {
    uart_info = USART1->SR;
    uart_info = USART1->DR;
    DMA_Cmd(DMA1_Channel5,DISABLE);
    dma_recv_num = 200 -  DMA_GetCurrDataCounter(DMA1_Channel5);
    DMA1_Channel5->CNDTR=200;
    DMA_Cmd(DMA1_Channel5,ENABLE);
    dma_receive_flag = 1;
 }
}


串口助手测试100ms循环发送接收5个字节无问题!


printf函数调用

int fputc(int ch, FILE *f)
{
  USART_SendData(USART1, (uint8_t) ch);
  while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
  return (ch);
}
//禁用半主机模式
#pragma import(__use_no_semihosting)
struct __FILE
{
  int handle;
};
 
FILE __stdout;
 
void _sys_exit(int x)
{
  x = x;
}

直接添加上述代码,就无需使用microlib库,因为很不建议用microlib库。


LL库串口相关。(LL库相对于HAL库更接近于底层,生成代码量更少,效率更高)

	
    SysTick_Config(64000);//内部晶振,开启systick中断
    LL_USART_EnableIT_RXNE(USART1);  //开启串口1接收中断
--------------------------------------------------------------
void Uart1_SendData(unsigned char data)
{
  USART1->SR;
  LL_USART_TransmitData8(USART1,data); 
  while(LL_USART_IsActiveFlag_TC(USART1)!=SET);
}
void Uart1_SendString(char *data)
{
  while(*data)
  {
    Uart1_SendData(*(data++));
  }
}
void Uart1_SendLength(char*DP,unsigned int num)
{
  for(int k=0;k<num;k++)
  {
    Uart1_SendData(DP[k]);
  }
}
------------------------------------------------------------------
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if(LL_USART_IsActiveFlag_RXNE(USART1))
	{
		RxBuffer[rx_count]=LL_USART_ReceiveData8(USART1);
		if(rx_count++>49)rx_count=0;
	}
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

猜你喜欢

转载自blog.csdn.net/coderdd/article/details/88670228
今日推荐