隨筆:項目總結 主要是STM32的一些應用

終於最近做的一個項目基本上結題了,因此有時間來對這個項目所涉及到的知識進行一個總結。

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 <stdio.h>
#include <stdlib.h>

#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´«Êä
}	  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章