這一章編寫DAC和ADC程序,即數模/模數轉換。程序中封裝了兩個DAC,各1個獨立通道,對應輸出腳爲PA4和PA5,提供兩個方法,ADDA::daDMA(Timer & tim)成員方法以DMA方式按預定數據生成兩個正弦波,通道1(PA4)是半幅波形,通道2(PA5)是全幅波形。 ADDA::da()成員方法把指定內存的數據轉換成模擬信號,未使用DMA,因爲已經是一一對應。
模數轉換使用ADC1轉換器,共有10個通道,採用硬件存儲DMA,不佔用CPU時間,包括8個端口通道,對應輸入爲PA0-7,其中PA4和PA5與DAC共用,可以從內部檢測DA/AD的正確性。還有兩個內部通道,CPU溫度和基準電壓。
值得一提的是,參考代碼中沒有 “DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; // 不限幅值” 這一行,造成daDMA程序有時能用,有時不能用,費了好多時間才找出問題。
ADDA.h
#ifndef __ADDA__
#define __ADDA__
extern "C" { // 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#pragma diag_default 368 // 恢復368號警告
}
#include "IO.h"
#include "Timer.h"
#define DAC_DHR12RD_Address 0x40007420 // DAC地址
#define ADC1_DR_Address ((uint32_t)0x4001244C) // ADC地址
//ADC_DR(ADC規則數據寄存器),偏移量=0x4c ADC1(0x40012400-0x400127ff)
// 設置通道1數據
#define SETDAC1( v ) *((__IO uint32_t *)(DAC_BASE + (u32)0x00000008 + DAC_Align_12b_R)) = (uint32_t)v
// 設置通道2數據
#define SETDAC2( v ) *((__IO uint32_t *)(DAC_BASE + (u32)0x00000014 + DAC_Align_12b_R)) = (uint32_t)v
class ADDA : public IO
{
// Construction
public:
ADDA();
// Properties
public:
u16 m_adData[10]; // 10個ADC數據 PA0-7,CPU內部溫度,內部基準電壓
private:
// Methods
public:
void daDMA(Timer & tim); // 用DMA生成正弦波
inline void ad(void); // ADc
inline void da(void); // DAC
// Overwrite
public:
};
#endif
ADDA.cpp
/**
******************************************************************************
* @file ADDA.cpp
* @author Mr. Hu
* @version V1.0.0 STM32F103VET6
* @date 06/07/2019
* @brief DMA,AD,DA
* @IO
* DMA2_Channel4 傳送ADC1轉換數據
* ADC1 數模轉換器1
* ADC通道0-7 外部模擬量輸入,對應PA0-5,其中PA4-5與DAC共用
* ADC通道16 CPU內部溫度
* ADC通道17 內部基準電壓值
* DAC通道1-2 模數轉換器1-2,對應PA4-5與ADC共用
* PA0-7 ADC1的0-7通道
* PA4-5 ADC1的4-5通道,同時也是DAC的1-2通道
******************************************************************************
* @remarks
* 基於DMA的ADC,硬件轉換和存儲,不佔CPU時間
* DAC有兩個方法,一個是單一數據轉換,未用DMA,因爲本來就是直接寫內存。另一個方法是按
* 指定數據生成正弦波,使用DMA。
* 實現8個模擬量輸入,2個模擬量輸出,其中PA4-5兩個IO口既是輸入,又是輸出,AD/DA共用,
* 可以從軟件中監視AD/DA的數據,實際應用用時只能用於DA輸出
*
* 參考資料
* https://blog.csdn.net/qq_38410730/article/details/80071349 ADC有端口分配圖
* https://blog.csdn.net/weixin_42653531/article/details/81123770
* https://blog.csdn.net/iteye_3759/article/details/82547927
* https://www.cnblogs.com/zhoubatuo/p/6118897.html
*/
/* Includes ------------------------------------------------------------------*/
extern "C" { // 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x_adc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_tim.h"
#include <stm32f10x.h>
#pragma diag_default 368 // 恢復368號警告
}
#include "IO.h"
#include "ADDA.h"
/**
* @date 06/07/2019
* @brief 數模/模數轉換
* IO(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AIN, 2)
* 佔用PA0-PA7共8個ADC端口,其中PA4-5與DAC共用。
* @param None
* @retval None
*/
ADDA::ADDA()
: IO(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AIN, 2) // GPIOx, nPin, GPIO_Mode_IPU 上拉, 2 輸入時無效
{
for(u16 i = 0; i < sizeof(m_adData)/sizeof(m_adData[0]); i++)
m_adData[i] = 0;
/* Enable peripheral clocks ------------------------------------------------*/
/* DMA2 clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
/* DAC Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
da(); // 啓動DAC
ad(); // 啓動ADC
}
/**
* @date 06/07/2019
* @brief 數模轉換,DMA方式按預定數據生成兩個正弦波,通道1(PA4)是半幅波形,通道2(PA5)是全幅波形。
* @param tim 觸發源
* @retval None
*/
void ADDA::daDMA(Timer & tim)
{
TIM_TypeDef * ti = tim.m_pTIMx;
assert_param( ti == TIM2
|| ti == TIM4
|| ti == TIM5
|| ti == TIM6
|| ti == TIM7
|| ti == TIM8
);
/* TIMx TRGO selection */
TIM_SelectOutputTrigger(ti, TIM_TRGOSource_Update);
u32 trigger = // 觸發源
ti != TIM2 ? ti != TIM4 ? ti != TIM5 ? ti != TIM6 ? ti != TIM7 ? ti != TIM8
? 0 // 異常
: DAC_Trigger_T8_TRGO
: DAC_Trigger_T7_TRGO
: DAC_Trigger_T6_TRGO
: DAC_Trigger_T5_TRGO
: DAC_Trigger_T4_TRGO
: DAC_Trigger_T2_TRGO
;
/* DAC channel1 Configuration */
// 特別提示,參考代碼中沒有給DAC_LFSRUnmask_TriangleAmplitude賦值,
// 造成有時能用,有時不能用,賦值以後正常
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger = trigger; // 設置觸發源
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; // 不產生波形
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; // 關閉輸出緩存
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; // 不限幅值
DAC_Init(DAC_Channel_1, &DAC_InitStructure); // 初始化通道1 PA4
DAC_Init(DAC_Channel_2, &DAC_InitStructure); // 初始化通道2 PA5
// 正弦波數據
const uint16_t Sine12bit[32] = { // 全幅
2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056,
3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909,
599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647};
const uint16_t Sine12bitlow[32] = { // 半幅
1024, 1224, 1415,1592,1749,1875,1970,2028,2048,2028,
1969, 1875, 1748,1592,1415,1224,1024,824,632,454,
300,172,78,19,0,19,78,172,300,454,632,824};
uint32_t DualSine12bit[32];
//* Fill Sine32bit table *
for (u8 Idx = 0; Idx < 32; Idx++)
{
DualSine12bit[Idx] = (Sine12bit[Idx] << 16) + (Sine12bitlow[Idx]);
}
//* DMA2 channel4 configuration *
// DMA通道與DAC對應,不能換
DMA_DeInit(DMA2_Channel4);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_Address; // 外設地址,雙通道模式,其他模式請看手冊
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit; // 原始數據指針,正弦數據
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 傳輸方向外設到內存
DMA_InitStructure.DMA_BufferSize = 32; // 緩存大小32個數據,每個數據對應兩個通道
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外設地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 內存地址自增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 外設傳輸雙字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 內存傳輸雙字
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循環輸出
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 優先級爲高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁止內存間傳輸
DMA_Init(DMA2_Channel4, &DMA_InitStructure); // 初始化
/* Enable DMA2 Channel4 */
DMA_Cmd(DMA2_Channel4, ENABLE);
/* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is
automatically connected to the DAC converter. */
DAC_Cmd(DAC_Channel_1, ENABLE);
/* Enable DAC Channel2: Once the DAC channel2 is enabled, PA.05 is
automatically connected to the DAC converter. */
DAC_Cmd(DAC_Channel_2, ENABLE);
/* Enable DMA for DAC Channel2 */
DAC_DMACmd(DAC_Channel_2, ENABLE);
}
/**
* @date 06/07/2019
* @brief 數模轉換,工作方式是循環轉換指定的數據
* SETDAC1和SETDAC2 分別設置兩個通道數據,模擬量輸出到PA4,PA5
* @param None
* @retval None
*/
void ADDA::da()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道時鐘
DAC_InitTypeDef DAC_InitType;
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用觸發功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形發生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值設置
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1輸出緩存關閉 BOFF1=1
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
DAC_Init(DAC_Channel_2,&DAC_InitType); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
DAC_Cmd(DAC_Channel_2, ENABLE); //使能DAC1
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右對齊數據格式設置DAC值
DAC_SetChannel2Data(DAC_Align_12b_R, 0); //12位右對齊數據格式設置DAC值
}
/**
* @date 06/07/2019
* @brief 數模轉換,用DMA方式,把結果存到m_adData[10]中
* m_adData[0]-[7]對應PA0-7的輸入電壓轉換結果
* PA4,PA5與DAC共用
* m_adData[8]-[9]分別的CPU溫度和內部基準電壓
* @param None
* @retval None
*/
// https://blog.csdn.net/nicholas_dlut/article/details/80937036
void ADDA::ad(void)
{
ADC_TempSensorVrefintCmd(ENABLE); //開啓內部溫度傳感器
/* Enable peripheral clocks ------------------------------------------------*/
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// DMA通道與ADC對應,不能換
/* DMA1 channel1 configuration ----------------------------------------------*/
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1); //選擇DMA的通道1
//設定從ADC外設的數據寄存器(ADC1_DR_Address)轉移到內存(ADCConcertedValue)
//每次傳輸大小16位,使用DMA循環傳輸模式
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; // 外設地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)m_adData;//數據緩衝區的地址
//外設爲數據源
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//數據緩衝區,大小2半字
DMA_InitStructure.DMA_BufferSize = 10;
// 外設地址固定
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//內存地址增加,多組adc時,使能,數據傳輸時,內存增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//半字
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//DMA循環傳輸
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
//優先級高
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//??
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//執行
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitTypeDef ADC_InitStructure;
//ADC獨立模式 相對於雙重模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//掃描模式用於多通道採集
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
//開啓連續轉換模式 當轉換完本組(可能是一個)繼續重新開始執行
//相對於單次模式:轉換一次後就結束
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
//不使用外部觸發轉換
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//採集數據右對齊
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//轉換組的通道數目
ADC_InitStructure.ADC_NbrOfChannel = 10;
//執行
ADC_Init(ADC1, &ADC_InitStructure);
//配置ADC時鐘,爲PCLK2的8分頻,即9Hz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/* ADC1 regular channel11 configuration */
//配置ADC1的通道11爲55.5個採樣週期
//默認組,adc1 ,通道11,排序爲1,55.5週期
//ADC1,ch17,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_0,1, ADC_SampleTime_239Cycles5);
//ADC1,ch16,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_1,2, ADC_SampleTime_239Cycles5);
//ADC1,ch4,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_2,3, ADC_SampleTime_239Cycles5);
//ADC1,ch5,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_3,4, ADC_SampleTime_239Cycles5);
//ADC1,ch6,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_4,5, ADC_SampleTime_239Cycles5);
//ADC1,ch7,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_5,6, ADC_SampleTime_239Cycles5);
//ADC1,ch6,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_6,7, ADC_SampleTime_239Cycles5);
//ADC1,ch7,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_7,8, ADC_SampleTime_239Cycles5);
//ADC1,ch10,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_16,9, ADC_SampleTime_239Cycles5);
//ADC1,ch11,序號1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_Channel_17,10, ADC_SampleTime_239Cycles5);
//----------------------使能溫度傳感器----------------------------
ADC_TempSensorVrefintCmd(ENABLE);
/* Enable ADC1 DMA */
//使能ADC_DMA
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
//使能ADC
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibration register */
//使能ADC1的復位校準寄存器
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
//等待校準完成
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibration */
//使能ADC1的開始校準寄存器
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
//等待完成
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
//使用軟件觸發,由於沒有采用外部觸發
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
Main.h
#ifndef __MAIN__
#define __MAIN__
extern "C" { // 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368 // 消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x.h"
#pragma diag_default 368 // 恢復368號警告
}
s32 m_nCPUTemperate; // CPU溫度 x 100
#endif
Main.cpp
/**
******************************************************************************
* @file Main.cpp
* @author Mr. Hu
* @version V1.0.0 STM32F103VET6
* @date 05/18/2019
* @brief 程序入口
* @io
* TIM3 PWM
* TIM4 Encode
* TIM7 通用定時器
* ADC1 ADC
* DAC1
* DAC2
*
* PA0-PA7 ADC
* PA4 DAC1輸出
* PA5 DAC2輸出
* PA6 ADC
* PA7 ADC
* PA9 板載串口
* PA10 板載串口
* PA13 板載JLINK佔用
* PA14 板載JLINK佔用
* PA15 板載JLINK佔用
*
* PB1 板載SW2
* PB3 板載JLINK佔用
* PB4 板載JLINK佔用,部分映像的通道1不能用,所以用了沒有得映像
* PB6 編碼器 A
* PB7 編碼器 B
* PB8 板載CAN
* PB9 板載CAN
* PB10 板載RS485
* PB11 板載RS485
* PB13 板載LED2
* PB14 板載LED3
* PB15 板載SW3
*
* PC4 板載RS485
* PC5 板載RS485
* PC6 PWM1
* PC7 PWM2
* PC8 PWM預留
* PC9 PWM預留
******************************************************************************
* @remarks
*
*/
extern "C" { // 兼容C,按C語言編譯,Keil5中的包含文件已經加入了C++兼容,不用再加這一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x_tim.h"
#include "stm32f10x_dac.h"
#pragma diag_default 368 // 恢復368號警告
}
#include "stm32f10x_adc.h"
#include "IO.h"
#include "Timer.h"
#include "GeneralTimer.h"
#include "BoardLED.h"
#include "PWM.h"
#include "MedianFilter.h"
#include "AverageFilter.h"
#include "ADDA.h"
#include "Main.h"
/**
* @date 05/18/2019
* @brief 主入口,主循環
* 如果不正常運行,可能是棧設置不夠 startup_stm32f10x_hd.s Stack_Size EQU 0x600
* @param None
* @retval None
*/
int main(void)
{
m_nCPUTemperate = 0;
SystemInit(); // 配置系統時鐘爲72M
GeneralTimer tim(TIM2); // 通用定時器,實際用TIM7,不佔用IO,但軟件仿真只有1-4,所以選2
ADDA adda; // 定時器下緊跟啓動ADDA,因爲轉換需要時間
//adda.daDMA(tim); // DMA方式,按數據生成正弦波,使用這個功能時,註釋下面的三角波代碼
s16 dainc = 1;
u16 daval = 0;
BoardLED boardLED( &tim ); // 板載LED
// 板載按鍵,PB1 SW2, PB2 SW3,不同的板子不一樣。
IO key(GPIOC, GPIO_Pin_1 | GPIO_Pin_15, GPIO_Mode_IPU, 2); // GPIOx, nPin, GPIO_Mode_IPU 上拉, 2 輸入時無效
// 使能按鍵濾波
//tim.inb[1].level = 1; // SW2 PB1 上拉
tim.inb[1].enable = 1; // SW2 PB1 使能
//tim.inb[15].level = 1; // SW3 PB15 上拉
tim.inb[15].enable = 1; // SW3 PB15
u32 loopCount = 0; // 主循環計數
PWM pwm;
//pwm.orthogonal( 2 - 1, 1000 - 1 ); // 18kHz 移相正交波形
for(int i = 0; i < 3600; i++) // 延時大約1ms,等待AD轉換後再往下接行,求平均時要以獲得比較準確的初值
{
i++; // 加一句,不然優化編譯時會被刪掉
}
// 計算方法
// float v2 = d * 5.f / 0xfff; // 把測量數d(0-ffff)轉換成電壓,單片機用了5V電源,所以用5.f,否則改用3.3f
// (1.43f - v2) / 0.0043 + 25; // 1.43f 25度時的電壓值,v2 測量值,0.0043 每度電壓變化
// 下面是簡化後的公式,因爲沒有FPU,不能用浮點計算,結果單位爲1/100度
#define CPUT ((s32)35756 - 1221 * adda.m_adData[8] / 43) /* adda.m_adData[8]是內部CPU溫度 */
MedianFilter mfTemperate( CPUT, 2 );
AverageFilter afTemperate( CPUT, 3 );
while(1)
{
tim.loop(); // 必須放在主循環的第一行,按鍵濾波和上下沿微分。
// PWM
pwm.setData(0, 300); // PWM1 PC6 30%的佔空比
pwm.setData(1, 700); // PWM2 PC7 70%的佔空比
// LED
// 測試時間
loopCount++;
if( !tim.m_t[2] ) // 定時器2
{
tim.m_t[2] = 1000; // 延時1000ms
boardLED.m_nNum = 100 * 1000 / loopCount; // 計算循環週期,1000*1000對應週期單位是1us,100*1000是10us,以此類推。
if( boardLED.m_nNum > 0xf )
boardLED.m_nNum = 0xf; // 大於15時,顯示15
loopCount = 0;
}
boardLED.showNumber(); // 顯示四位二進制boardLED.m_nNum,用了m_t[0]
// CPU溫度 https://blog.csdn.net/qq_27970103/article/details/81325418
if(!tim.m_t[3])
{
s32 mf = mfTemperate.filter( CPUT ); // 中值濾波
m_nCPUTemperate = afTemperate.filter( mf ); // 平均濾波,效果最好
tim.m_t[3] = 100; // 100ms 計算一次
}
// 開關LED
if( tim.inb[1].down | tim.inb[15].down ) // 兩個板載開關的下降沿
{
boardLED.showLED(GPIO_Pin_14, 1); // 點亮LED3
}
else if( tim.inb[1].up | tim.inb[15].up ) // 兩個板載開關的上升沿
{
boardLED.showLED(GPIO_Pin_14, 0); // 熄滅LED3
}
// DA-AD 測試,先設置數據,用DA轉換成電壓,再用AD轉換成數字,用示波器觀察,延後1ms
// 產生三角波
SETDAC2( daval );
daval += dainc;
if(daval > 4095) // daval是無符號數,減過0以後是很大的數,所以只用一個判斷
{
dainc = -dainc; // 改變方向
daval += dainc; // 調到範圍內
}
u16 test1 = adda.m_adData[5]; // adda.m_adData[5]是PA5電壓的轉換結果,而PA5的電壓是數字adda.m_daData.da2的轉換結果,用了同一個IO腳,不用接線測試
SETDAC1(test1); // 再把結果送到DAC通道1(adda.m_daData.da1 = test1)PA4,再用示波器觀查,延後1ms,DA觸發是1ms
}
}
Main.cpp中下面的代碼是獲取CPU溫度,每100ms採樣一次,先用中值濾波,去掉高幅值偶發性干擾,再用均值濾波減弱隨機噪聲,得到比較穩定的溫度值,實測±0.6度內跳動。
// CPU溫度 https://blog.csdn.net/qq_27970103/article/details/81325418
if(!tim.m_t[3])
{
s32 mf = mfTemperate.filter( CPUT ); // 中值濾波
m_nCPUTemperate = afTemperate.filter( mf ); // 平均濾波
tim.m_t[3] = 100; // 100ms 計算一次
}
下面的代碼是ADC/DAC應用,daval是0-4095遞加和遞減的數據,通過SETDAC2(daval)變成0-5V的模擬信號,輸出到PA5腳,adda.m_adData[5]把PA5的電壓轉換成數字,SETDAC1(test1)把這個數字轉換電壓,輸出到PA4。下圖是PA4和PA5的示波器測量波形。兩個腳的波形差1ms。
// DA-AD 測試,先設置數據,用DA轉換成電壓,再用AD轉換成數字,用示波器觀察,延後1ms
// 產生三角波
SETDAC2( daval );
daval += dainc;
if(daval > 4095) // daval是無符號數,減過0以後是很大的數,所以只用一個判斷
{
dainc = -dainc; // 改變方向
daval += dainc; // 調到範圍內
}
u16 test1 = adda.m_adData[5]; // adda.m_adData[5]是PA5電壓的轉換結果,而PA5的電壓是數字adda.m_daData.da2的轉換結果,用了同一個IO腳,不用接線測試
SETDAC1(test1); // 再把結果送到DAC通道1(adda.m_daData.da1 = test1)PA4,再用示波器觀查,延後1ms,DA觸發是1ms
註釋上面的三角波發生器,啓用 adda.daDMA(tim); 以DMA方式生成正弦波,如下圖,因爲數據較小,鋸齒較大。
STM32實戰系列源碼,按鍵/定時器/PWM/ADC/DAC/DMA/濾波
STM32實戰一 初識單片機
STM32實戰二 新建工程
STM32實戰三 C++ IO.cpp
STM32實戰四 定時器和按鍵
STM32實戰五 板載LED顯示數據
STM32實戰六 PWM加移相正交
STM32實戰七 數字濾波
STM32實戰八 DAC/ADC
STM32實戰九 編碼器
STM32開發過程的常見問題