終於最近做的一個項目基本上結題了,因此有時間來對這個項目所涉及到的知識進行一個總結。
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´«Êä
}