STM32通用部分源文件
- sys文件夹代码介绍
- delay文件夹代码介绍
- usart文件夹代码介绍
- malloc文件夹代码介绍
参考资料:
本博客的内容和程序主要参考正点原点 hal 库开发手册
以下实例使用stm32l4中的内部时钟进行工作,可直接使用STM32cube图形化配置:
sys文件夹代码介绍
sys 文件夹内包含了 sys.c 和 sys.h 两个文件。在 sys.h 里面定义了 STM32 的时钟配置函数(不可缺少)和汇编函数。
sys.h
#ifndef _SYS_H
#define _SYS_H
#include "stm32l4xx.h"
//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持OS
///
//定义一些常用的数据类型短关键字
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
typedef const int32_t sc32;
typedef const int16_t sc16;
typedef const int8_t sc8;
typedef __IO int32_t vs32;
typedef __IO int16_t vs16;
typedef __IO int8_t vs8;
typedef __I int32_t vsc32;
typedef __I int16_t vsc16;
typedef __I int8_t vsc8;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef const uint32_t uc32;
typedef const uint16_t uc16;
typedef const uint8_t uc8;
typedef __IO uint32_t vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t vu8;
typedef __I uint32_t vuc32;
typedef __I uint16_t vuc16;
typedef __I uint8_t vuc8;
void _Error_Handler(char *, int);
#define Error_Handler() _Error_Handler(__FILE__, __LINE__)
/* Configure the System clock to have a frequency of 80 MHz */
void SystemClock_Config(void);
//以下为汇编函数
void WFI_SET(void); //执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void); //开启所有中断
void MSR_MSP(u32 addr); //设置堆栈地址
#endif
sys.c
#include "sys.h"
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSI16)
* SYSCLK(Hz) = 80000000
* HCLK(Hz) = 80000000
* AHB Prescaler = 1
* APB1 Prescaler = 1
* APB2 Prescaler = 1
* HSI16 Frequency(Hz) = 16000000
* PLL_M = 4
* PLL_N = 40
* PLL_R = 2
* PLL_P = 7
* PLL_Q = 4
* Flash Latency(WS) = 4
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* HSI16 is enabled after System reset, activate PLL with HSI16 as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLP = 7;
RCC_OscInitStruct.PLL.PLLQ = 4;
if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
while(1);
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
while(1);
}
}
/************************ Debug Handle Functions ***********************/
/**
* @brief This function is executed in case of error occurrence.
* @param file: The file name as string.
* @param line: The line in file as a number.
* @retval None
*/
void _Error_Handler(char *file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
// printf("Occur to error: file %s on line %d\r\n", file, line);
while(0)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
printf("Wrong parameters value: file %s on line %d\r\n", file, line);
/* Infinite loop */
while (1)
{
}
}
#endif
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
__asm void WFI_SET(void)
{
WFI;
}
//关闭所有中断(但是不包括fault和NMI中断)
__asm void INTX_DISABLE(void)
{
CPSID I
BX LR
}
//开启所有中断
__asm void INTX_ENABLE(void)
{
CPSIE I
BX LR
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
delay文件夹代码介绍
delay 文件夹内包含了 delay.c 和 delay.h 两个文件,这两个文件用来实现系统的延时功能。使用 systick 时钟的中断与非中断两种方法进行延迟,而不使用低效率的程序延迟等待。以下使用非中断的方式:
delay.h
#ifndef _DELAY_H
#define _DELAY_H
#include <sys.h>
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif
delay.c
#include "delay.h"
#include "sys.h"
static u8 fac_us=0; //us delay frequency times.
/***************************************************************************//**
* @brief Delay init function
*
* @param SYSCLK - System clock frequency select,and unit MHz.
*
* @return none.
*******************************************************************************/
void delay_init(u8 SYSCLK)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK;
}
//Delay nus function (note the range of nms)
//SysTick->LOAD为24bit寄存器,其范围: nms < 16777215/fac_us = 209000
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//Delay nms function
void delay_ms(u16 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
usart文件夹代码介绍
usart 文件夹内包含了 usart.c 和 usart.h 两个文件。这两个文件用于串口的初始化和中断接收。
usart.h
#ifndef _USART_H
#define _USART_H
#include "stm32l431xx.h"
#include "sys.h"
#include "stdio.h"
/******************************************************************************/
/******************************** usart *************************************/
/******************************************************************************/
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
#define RXBUFFERSIZE 1 //缓存大小
/******************************************************************************/
/************************ Variables Definitions *******************************/
/******************************************************************************/
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8 USART_RX_Flag; //接收完成标志
extern u16 USART_RX_Count; //接收数据计数器
extern u8 aRxBuffer[RXBUFFERSIZE]; //HAL库USART接收Buffer
extern UART_HandleTypeDef UART1_Handler; //UART句柄
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(USART_TypeDef * uart, u32 bound);
void Usart1Send(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
#endif
usart.c
#include "usart.h"
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN] = {0}; //接收缓冲,最大USART_REC_LEN个字节.
u8 USART_RX_Flag = 0; //接收完成标志
u16 USART_RX_Count = 0; //接收数据计数器
u8 aRxBuffer[RXBUFFERSIZE]; //HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄
/***************************************************************************//**
* @brief USART init function
*
* @param uart - uart channel to select.
* @param bound - uart baudrate to transmit.
*
* @return none.
*******************************************************************************/
void uart_init(USART_TypeDef * uart, u32 bound)
{
UART1_Handler.Instance=uart;
UART1_Handler.Init.BaudRate=bound;
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;
UART1_Handler.Init.StopBits=UART_STOPBITS_1;
UART1_Handler.Init.Parity=UART_PARITY_NONE;
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;
UART1_Handler.Init.Mode=UART_MODE_TX_RX;
if (HAL_UART_Init(&UART1_Handler) != HAL_OK) //HAL_UART_Init() function including enable uart
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}
//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9 | GPIO_PIN_10; //PA9 和 PA10
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9 和 PA10
#if EN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3
#endif
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
USART_RX_BUF[USART_RX_Count] = aRxBuffer[0];
USART_RX_Count++;
if(aRxBuffer[0] == '\n') {
USART_RX_Flag = 1;
// printf("yes");
}
if(USART_RX_Count > 10) {
Usart1Send(&UART1_Handler, USART_RX_BUF, USART_RX_Count);
USART_RX_Count = 0;
USART_RX_Flag = 0;
}
}
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
u32 timeout=0;
HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数
timeout=0;
while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
{
timeout++;超时处理
if(timeout>HAL_MAX_DELAY) break;
}
timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
{
timeout++; //超时处理
if(timeout>HAL_MAX_DELAY) break;
}
}
#endif
/* uart interrupt send data */
void Usart1Send(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
HAL_UART_Transmit(&UART1_Handler, pData, Size, 10); //10ms
}
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&UART1_Handler, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
malloc文件夹代码介绍
STM32内存管理:
- 由于malloc和free是标准的库函数,使用时容易造成内存碎片,盲目使用会造成内存溢出
- 单片机的内存都比较小,而且没有MMU,可能会因为空间不足而分配失败,导致系统崩溃
- 单片机中如果是大的内存分配,而且malloc和free的次数不是特别频繁,可以直接调用库函数
- 如果对内存频繁操作,最好自己实现一个内存管理
malloc.h
#ifndef _MALLOC_H
#define _MALLOC_H
#include "sys.h"
#ifndef NULL
#define NULL 0
#endif
//定义三个内存池
#define SRAMIN 0 //内部内存池
#define SRAMEX 1 //外部内存池
#define SRAMCCM 2 //CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
#define SRAMBANK 3 //定义支持的SRAM块数
//mem1内存参数设定,mem1完全处于内部SRAM里面
#define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM1_MAX_SIZE 100*1024 //最大管理内存 110k
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存表大小
//mem2内存参数设定,mem2处于外部SRAM里面
#define MEM2_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM2_MAX_SIZE 200*1024 //最大管理内存 200k
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //内存表大小
//mem3内存参数设定,mem3处于CCM,用于管理CCM(特别注意,这部分SRAM,近CPU可以访问)
#define MEM3_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM3_MAX_SIZE 60*1024 //最大管理内存 60k
#define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE //内存表大小
//内存管理控制器
struct _m_mallco_dev
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //内存使用率
u8 *membase[SRAMBANK]; //内存池,管理SRAMBANK个区域的内存
u16 *memmap[SRAMBANK]; //内存状态表
u8 memrdy[SRAMBANK]; //内存管理是否就绪
};
extern struct _m_mallco_dev mallco_dev; //在malloc.c里面定义
void mymemset(void *s,u8 c,u32 count); //设置内存
void mymemcpy(void *des,void *src,u32 n);//复制内存
void mymem_init(u8 memx); //内存管理初始化函数(外/内部调用)
u32 mymem_malloc(u8 memx,u32 size); //内存分配(内部调用)
u8 mymem_free(u8 memx,u32 offset); //内存释放(内部调用)
u8 mem_perused(u8 memx); //获得内存使用率(外/内部调用)
//用户调用函数
void myfree(u8 memx,void *ptr); //内存释放(外部调用)
void *mymalloc(u8 memx,u32 size); //内存分配(外部调用)
void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配内存(外部调用)
#endif
malloc.c
#include "malloc.h"
//内存池(4字节对齐)
__align(4) u8 mem1base[MEM1_MAX_SIZE];
__align(4) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0x68000000))); //外部SRAM内存池
__align(4) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0x10000000))); //内部CMM内存池
//内存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE))); //外部SRAM内存池MAP
u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE))); //内部CCM内存池MAP
//内存管理参数
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //内存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE}; //内存分块大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE}; //内存总大小
//内存管理控制器
struct _m_mallco_dev mallco_dev=
{
mymem_init, //内存初始化
mem_perused, //内存使用率
mem1base,mem2base,mem3base, //内存池
mem1mapbase,mem2mapbase,mem3mapbase,//内存管理状态表
0,0,0, //内存管理未就绪
};
//复制内存
//*des:目的地址
//*src:源地址
//n:需要复制的内存长度(字节为单位)
void mymemcpy(void *des,void *src,u32 n)
{
u8 *xdes = des;
u8 *xsrc = src;
while(n--) *xdes++ = *xsrc++;
}
//设置内存
//*s:内存首地址
//c :要设置的值
//count:需要设置的内存大小(字节为单位)
void mymemset(void*s,u8 c,u32 count)
{
u8 *xs = s;
while(count--) *xs++=c;
}
//内存管理初始化
//memx:所属内存块
void mymem_init(u8 memx)
{
mymemset(mallco_dev.memmap[memx],0,memtblsize[memx]*2); //内存状态表清零
mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零
mallco_dev.memrdy[memx]=1; //内存管理初始化OK
}
//获取内存使用率
//memx:所属内存块
//返回值:使用率(0~100)
u8 mem_perused(u8 memx)
{
u32 used=0;
u32 i;
for(i=0;i<memtblsize[memx];i++)
{
if(mallco_dev.memmap[memx][i])used++;
}
return (used*100)/(memtblsize[memx]);
}
//内存分配(内部调用)
//memx:所属内存块
//size:要分配的内存大小(字节)
//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
u32 mymem_malloc(u8 memx,u32 size)
{
signed long offset=0;
u16 nmemb; //需要的内存块数
u16 cmemb=0;//连续空内存块数
u32 i;
if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先执行初始化
if(size==0)return 0XFFFFFFFF;//不需要分配
nmemb=size/memblksize[memx]; //获取需要分配的连续内存块数
if(size%memblksize[memx])nmemb++;
for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区
{
if(!mallco_dev.memmap[memx][offset])cmemb++;//连续空内存块数增加
else cmemb=0; //连续内存块清零
if(cmemb==nmemb) //找到了连续nmemb个空内存块
{
for(i=0;i<nmemb;i++) //标注内存块非空
{
mallco_dev.memmap[memx][offset+i]=nmemb;
}
return (offset*memblksize[memx]);//返回偏移地址
}
}
return 0XFFFFFFFF;//未找到符合分配条件的内存块
}
//释放内存(内部调用)
//memx:所属内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败;
u8 mymem_free(u8 memx,u32 offset)
{
int i;
if(!mallco_dev.memrdy[memx])//未初始化,先执行初始化
{
mallco_dev.init(memx);
return 1;//未初始化
}
if(offset<memsize[memx])//偏移在内存池内.
{
int index=offset/memblksize[memx]; //偏移所在内存块号码
int nmemb=mallco_dev.memmap[memx][index]; //内存块数量
for(i=0;i<nmemb;i++) //内存块清零
{
mallco_dev.memmap[memx][index+i]=0;
}
return 0;
}else return 2;//偏移超区了.
}
//释放内存(外部调用)
//memx:所属内存块
//ptr:内存首地址
void myfree(u8 memx,void *ptr)
{
u32 offset;
if(ptr==NULL)return;//地址为0.
offset=(u32)ptr-(u32)mallco_dev.membase[memx];
mymem_free(memx,offset);//释放内存
}
//分配内存(外部调用)
//memx:所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址.
void *mymalloc(u8 memx,u32 size)
{
u32 offset;
offset=mymem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else return (void*)((u32)mallco_dev.membase[memx]+offset);
}
//重新分配内存(外部调用)
//memx:所属内存块
//*ptr:旧内存首地址
//size:要分配的内存大小(字节)
//返回值:新分配到的内存首地址.
void *myrealloc(u8 memx,void *ptr,u32 size)
{
u32 offset;
offset=mymem_malloc(memx,size);
if(offset==0XFFFFFFFF)return NULL;
else
{
mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size); //拷贝旧内存内容到新内存
myfree(memx,ptr); //释放旧内存
return (void*)((u32)mallco_dev.membase[memx]+offset); //返回新内存首地址
}
}