这一章编写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 "" 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 "" defines no constructor to initialize the following:
#include "stm32f10x_adc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_tim.h"
#include
#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 "" 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 "" 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开发过程的常见问题