自定義方波波形發生器(代碼庫)
本文講述一個可以產生任意波形的波形發生器,可以適用於m433,紅外發射等需要自定義波形的場景。且本文的代碼是非阻塞的,在波形生成期間可以繼續執行其他代碼。具體代碼:https://github.com/zrw269113179/waveform.git
注意,本文使用的所有相關於GPIO的操作都使用我的另一個底層驅動,詳見
https://blog.csdn.net/u014723040/article/details/90715769
預覽
讓我們先來看看效果,首先我們先定義一個波形對象
static waveform wave1;
然後對他進行初始化
waveform_init(&wave1,27);//27爲我們綁定的引腳號,即通過27號引腳輸出波形
接着再定時器中斷中調用輪詢函數
if (TIM2_GetITStatus(TIM2_IT_Update) == SET)
{
waveform_loop();
TIM2_ClearITPendingBit(TIM2_IT_Update);
}
記得先把27號腳配置成輸出模式,然後我們就可以調用函數生成波形了
waveform_generate(&wave1,0,100);
waveform_generate(&wave1,1,200);
waveform_generate(&wave1,0,300);
waveform_generate(&wave1,1,400);
waveform_generate(&wave1,0,500);
這裏我們的定時器週期爲100us,所以上述代碼波形先輸出10ms的低電平,再20ms的高電平,再30ms的低電平,再40ms的高電平,最後50ms的低電平,後續沒有其他輸出,因此保持低電平。如下圖所示
代碼分析
結構體
我們先來看這個結構體
#define WAVE_DEEP_LEN 20
typedef struct
{
unsigned short tick; // 輸出時間
unsigned char level:1; // 輸出電平
}wave_queue_obj;
typedef struct waveform_t
{
unsigned char pin; // 綁定引腳
unsigned char deep; // 隊列深度
unsigned char front; // 隊列頭
unsigned short tick_cnt; // 計時時間
struct waveform_t* next; // 鏈表
wave_queue_obj queue[WAVE_DEEP_LEN]; // 隊列數組
}waveform;
這裏我們可以看到定義了一個隊列,用隊列來記錄我們需要輸出的波形,因此我們可能需要更改WAVE_DEEP_LEN的值,使隊列不佔用過多的空間,推薦定義爲一個波形調waveform_generate函數的最大次數。即示例中的波形需要調用5次waveform_generate才能生成一次波形,那麼WAVE_DEEP_LEN就可以設置成5,這裏永遠取最大的值。
waveform_init
/**
* @brief 波形對象初始化
*
* @param wave 波形對象
* @param pin 輸出引腳
*/
void waveform_init(waveform *wave, unsigned char pin)
{
wave->front = 0;
wave->deep = 0;
wave->pin = pin;
wave->tick_cnt = 0;
if (head == 0x00)
{
head = wave;
}
else
{
wave->next = head;
head = wave;
}
}
初始化函數令波形對象*wave 綁定輸出引腳,並且將其編入鏈表,以便後續遍歷。
waveform_generate
/**
* @brief 波形生成函數
*
* @param wave 波形
* @param level 高低電平輸出
* @param tick 輸出時間
*/
void waveform_generate(waveform *wave, unsigned char level, unsigned short tick)
{
if ((wave->deep + 1) % WAVE_DEEP_LEN != wave->front)
{
wave->queue[wave->deep].tick = tick;
wave->queue[wave->deep].level = level;
wave->deep++;
if (wave->deep >= WAVE_DEEP_LEN)
{
wave->deep -= WAVE_DEEP_LEN;
}
}
}
這裏我們看到,僅僅是將輸出屬性入隊,而沒有直接做輸出。
waveform_loop
要做到非阻塞,就必須使用到輪詢,我們定義一個定時器來進行輪詢,waveform_loop即爲其輪詢函數,在定時器中定期調用該函數。
void waveform_loop()
{
waveform *iterator = head;
while (iterator != 0)
{
if (iterator->deep != iterator->front)
{
wave_queue_obj *temp = &iterator->queue[iterator->front];
pin_write(iterator->pin,temp->level);
iterator->tick_cnt++;
if (iterator->tick_cnt > temp->tick)
{
waveform_quit_queue(iterator);
iterator->tick_cnt=0;
}
}
iterator = iterator->next;
}
}
這裏我們遍歷鏈表,當iterator->deep != iterator->front
時,即該波形對象中輸出隊列不爲空時,我們輸出當前隊列需要輸出的電平,即pin_write(iterator->pin,temp->level);
,然後疊加計時時間iterator->tick_cnt++;
然後判斷輸出時間是否達到隊列中期望輸出的時間,如果大於該時間則隊列中當前對象出列,完成當前隊列的輸出。
總結
該代碼庫中我們通過對隊列和鏈表的運用,利用定時器的特點實現了非阻塞式的波形生成器,且調用簡單,適用於裸機或單線程生成自定義波形。