随笔:项目总结 主要是STM32的一些应用

终于最近做的一个项目基本上结题了,因此有时间来对这个项目所涉及到的知识进行一个总结。

文章目录

      • cJSON
      • UCOS-II
      • 软件FIFO
      • string stdio stdlib 微库函数
      • STM32的驱动程序DMA和GPIO AF

cJSON

cJSON的内容具体见我的另一篇文章,比较详细,这里就不叙述了。

UCOS-II

首先在这个项目里,我在STM32的程序中移植了一个UCOS-II的系统。关于UCOS-II操作系统的移植,可以参考网上的教程,例如正点原子的例程等,这方面的知识网上还是比较多的。具体的流程就不论述了,这里主要是对UCOS-II实际使用过程中遇到的问题进行一个解答。

1.首先对于UCOS-II的任务堆栈的大小,因为在进行任务调度的时候,我们会把当前任务压到一个堆栈中存放,因此堆栈的大小应该是大于当前任务的代码量的,换句话说一个任务的代码量越多、中间变量越多,那么这个任务所需要的堆栈空间越大。此外在进行内存配置之前,我们应该在hd.h文件中先按照自己的硬件能力,修改堆栈的大小。

2.对于UCOS-II来说,可能会出现以下情况,那就是,当同时使能的任务个数超过一定数目时,后面定义的任务可能不会执行。这是因为我们在进行UCOS-II正常运行之前,需要对UCOS-II进行配置,这个配置是在os_cfg.h文件中进行。例如最多任务个数,最大任务堆栈等参数等。

3.UCOS-II对于中断的使用,UCOS-II的中断必须要在进入中断时使用中断进入函数OSIntEnter(),在离开中断时使用中断离开函数OSIntExit(),否则的话就会造成UCOS-II的时序的紊乱。

4.UCOS-II的各个任务的优先级和周期的安排。对于UCOS-II优先级和周期的安排,主要我们要遵循以下原则,首先是底层程序,如通讯数据的接收等程序的优先级较高,周期较短,但是注意这里应该合理安排底层的执行时间,以避免CPU时间始终得不到释放,原则就是,在一个较长的时间范围内,所有的程序都能轮得到执行,没有哪一个程序长时间的占用CPU(例如某程序优先级高,执行时间长,同时执行周期短)。在此基础上,重要的程序优先级较高,次要的程序优先级较低。

软件FIFO

这个项目中用到了一个软件FIFO用于存储通讯峰值较高时的缓存通讯内容。这里把自己写的FIFO的代码贴出来。
首先是.h文件:

#ifndef __FIFO_H
#define __FIFO_H
#include "stm32f10x.h"
#include 
#include 

#ifndef NULL
#define NULL 0
#endif	 

typedef struct 
{
	u8 size_max            ;       //FIFO的最大个数
	char*  point_queue[10]       ;    //存放FIFO指针的队列表
	u16    len_queue[10]         ;     //各个指针内容的长度队列表
	u8   size_now                ;        //当前第一位的指针的长度
	u8   empty_if                ;        //是否空标志位
	u8   enough_if               ;      //是否满标志位
	u16  percent                 ;      //当前FIFO的使用率
} FIFOtype;    

typedef struct 
{
	char*  point ;
	u16   len    ;   
} FIFO_OUTtype;    

u8 FIFO_In(FIFOtype* FIFO,char* p,u16 len);
FIFO_OUTtype FIFO_Out(FIFOtype* FIFO);
void FIFO_Free(FIFO_OUTtype* FIFO);

#endif

然后是.c文件

#include "fifo.h"	    

//入队列函数
//输入值: FIFO,指针,长度
//返回值:0 成功 1 失败
u8 FIFO_In(FIFOtype* FIFO,char* p,u16 len)
{
	u8 temp=0;
	if((*FIFO).enough_if!=1)             //Èç¹ûFIFOδÂú
	{
		(*FIFO).point_queue[(*FIFO).size_now]=p;
		(*FIFO).len_queue[(*FIFO).size_now]=len;
		(*FIFO).size_now++;
	  (*FIFO).empty_if=0;
		if((*FIFO).size_now>=(*FIFO).size_max)
		{
			(*FIFO).enough_if=1;
		}
		else
		{
			(*FIFO).enough_if=0;
		}
		(*FIFO).percent=((u16)(*FIFO).size_now*100)/(u16)(*FIFO).size_max;
		temp=1;
	}
	return temp;
}


//FIFO出队列函数
//输入值:FIFO
//返回值:FIFO_OUT(包含指针和长度)
FIFO_OUTtype FIFO_Out(FIFOtype* FIFO)
{
	FIFO_OUTtype temp={0,0};
	u8 t;
	if((*FIFO).empty_if!=1)         //Èç¹ûFIFO²»Îª¿Õ
	{
		temp.point=(*FIFO).point_queue[0];
		temp.len=(*FIFO).len_queue[0];
		(*FIFO).size_now--;
		for(t=0;t<(*FIFO).size_now;t=t+1)
		{
			(*FIFO).point_queue[t]=(*FIFO).point_queue[t+1];
			(*FIFO).len_queue[t]=(*FIFO).len_queue[t+1];
		}
		(*FIFO).point_queue[(*FIFO).size_now]=0;
		(*FIFO).len_queue[(*FIFO).size_now]=0;
		if((*FIFO).size_now==0)
		{
		  (*FIFO).empty_if=1;
		}
		else
		{
			(*FIFO).empty_if=0;
		}
		(*FIFO).enough_if=0;
		(*FIFO).percent=((u16)(*FIFO).size_now*100)/(u16)(*FIFO).size_max;		
	}
	return temp;
}

//FIFO释放内存函数
//输入值:FIFO_OUT(指针长度)
//返回值:无
void FIFO_Free(FIFO_OUTtype* FIFO)
{
	free((*FIFO).point);
	(*FIFO).len=0;
}

string stdio stdlib 微库函数

这里用到了许多C语言的库,例如string、stdio、stdlib等函数,那么具体各个库函数里面的函数是什么,源码什么的大家网上也都能搜的到,那么这里主要还是写一写自己在使用这些函数时的心得体会。

首先是常用的申请内存和释放内存,这里注意申请内存和释放内存一般来说只能做一次,如果对一个指针重复释放,有可能造成硬件错误。这里需要对程序有个总体的把握,对内存的使用有一个生命周期的把握。

然后是string里的函数,首先是sizeof和strlen的区别,sizeof其实求的是后面的输入变量所占的空间的大小。如果后面跟的是一个char*的指针,那么sizeof的值就是32(对于32位的操作系统来说)。如果sizeof后面跟的是一个结构体,那么sizeof的值就是结构体所占的内存空间的大小。而strlen后面跟的是(字符串)指针,返回的值就是从当前指针开始,到遇到 0x00 为止,中间的字符的个数。这两者使用起来还是有很大差别的,尤其是对于初学者来说,可能常常容易使用错。

然后是对于字符串,在字符串的最后一个字符后一定要有一位 0x00 ,否则在使用string函数的时候,会出现不可预知的错误。

然后是stdio函数,这个头文件主要是在使用printf和screnf等函数时,必须添加。

STM32的驱动程序DMA和GPIO AF

这里主要介绍两个驱动程序,一个是USART的DMA配置方法。

首先是DMA的配置:

///////////////////////////////////////USART1 DMA·¢ËÍÅäÖò¿·Ö//////////////////////////////////	   		    
//DMA1µÄ¸÷ͨµÀÅäÖÃ
//ÕâÀïµÄ´«ÊäÐÎʽÊǹ̶¨µÄ,ÕâµãÒª¸ù¾Ý²»Í¬µÄÇé¿öÀ´ÐÞ¸Ä
//´Ó´æ´¢Æ÷->ÍâÉèģʽ/8λÊý¾Ý¿í¶È/´æ´¢Æ÷ÔöÁ¿Ä£Ê½
//DMA_CHx:DMAͨµÀCHx
//cpar:ÍâÉèµØÖ·
//cmar:´æ´¢Æ÷µØÖ·    
void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar)
{
	DMA_InitTypeDef DMA_InitStructure;
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//ʹÄÜDMA´«Êä
  DMA_DeInit(DMA_CHx);   //½«DMAµÄͨµÀ1¼Ä´æÆ÷ÖØÉèΪȱʡֵ
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMAÍâÉèADC»ùµØÖ·
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMAÄÚ´æ»ùµØÖ·
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //Êý¾Ý´«Êä·½Ïò£¬´ÓÄÚ´æ¶ÁÈ¡·¢Ë͵½ÍâÉè
	DMA_InitStructure.DMA_BufferSize = 0;  //DMAͨµÀµÄDMA»º´æµÄ´óС
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //ÍâÉèµØÖ·¼Ä´æÆ÷²»±ä
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //ÄÚ´æµØÖ·¼Ä´æÆ÷µÝÔö
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //Êý¾Ý¿í¶ÈΪ8λ
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Êý¾Ý¿í¶ÈΪ8λ
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //¹¤×÷ÔÚÕý³£»º´æģʽ
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMAͨµÀ xÓµÓÐÖÐÓÅÏȼ¶ 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMAͨµÀxûÓÐÉèÖÃΪÄÚ´æµ½ÄÚ´æ´«Êä
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //¸ù¾ÝDMA_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯DMAµÄͨµÀUSART1_Tx_DMA_ChannelËù±êʶµÄ¼Ä´æÆ÷	
} 

然后是串口的配置,这里我使用了TIMER作为MODBUS的定时器,可以不去管,需要注意的是,USART和DMA是有一个对应关系的,比如在STM32F103RC中,USART1对应的DMA1_Channel4,USART2对应的DMA1_Channel7,USART3对应的DMA1_Channel2,具体是什么对应,还需要仔细阅读官方数据手册:

void Usart1_TIM4_Init(u32 bound,u16 arr,u16 psc)
{
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure; 
	NVIC_InitTypeDef NVIC_InitStructure;	
	
	//ÍâÉèµÄʱÖÓʹÄÜ
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//ʹÄÜUSART1ʱÖÓ	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //ʹÄÜPORTAʱÖÓ	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);   //ʹÄÜPORTCʱÖÓ	
	
	
	//GPIOÉèÖÃ
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//PA9¸´ÓÃÍÆÍìÊä³ö¹¦ÄÜ£¬ËÙ¶È50MHZ TX
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	//PA10¸¡¿ÕÊäÈ빦ÄÜ£¬ËÙ¶È50MHZ 	RX
	
	//´®¿Ú¹¤×÷ģʽÅäÖÃ
	USART_InitStructure.USART_BaudRate = bound;			//²¨ÌØÂÊ					
	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(USART1, &USART_InitStructure);				
	
	USART_ClearFlag(USART1, USART_FLAG_TC);
	
	
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  	//ʹÄÜ´®¿Ú2µÄDMA·¢ËÍ
	UART_DMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)USART1_TX_BUF);//DMA1ͨµÀ7,ÍâÉèΪ´®¿Ú2,´æ´¢Æ÷ΪUSART2_TX_BUF 

	
	//ÖжÏÉèÖÃ			 
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;       //µÍÓÅÏȼ¶±ðµÄÖжÏ
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;							//ÏìÓ¦Öжϵȼ¶Îª0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);  
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);		              //ʹÄÜUSART1µÄ½ÓÊÕÖжϵÄ״̬¼Ä´æÆ÷
	
	TIM4_Init(arr,psc);	//10msÖжÏÒ»´Î
	TIM_Cmd(TIM4, DISABLE);  //¹Ø±ÕʱÖÓ
	USART1_RX_STA=0;
		
	USART_Cmd(USART1, ENABLE);	                                    //¿ªÆôUSART1	
}

以及DMA的发送函数

//¿ªÆôÒ»´ÎDMA´«Êä
void UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u16 len)
{
	DMA_Cmd(DMA_CHx, DISABLE );  //¹Ø±Õ ָʾµÄͨµÀ        
	DMA_SetCurrDataCounter(DMA_CHx,len);//DMAͨµÀµÄDMA»º´æµÄ´óС	
	DMA_Cmd(DMA_CHx, ENABLE);           //¿ªÆôDMA´«Êä
}	  

你可能感兴趣的:(STM32)