本文介绍了极海 APM32F402R Micro-EVB 开发板通过 LabVIEW 上位机实现 ADC 电压和温度数据采集的项目设计,采用串口发送和串口中断查询两种方案,包括项目介绍、工程调试、串口中断测试、LabVIEW 上位机设计、程序测试及数据保存等
项目介绍
开发板工程调试:串口连续打印 ADC 电压转换值和温度值;
LabVIEW 上位机设计:包括前面板和程序框图的设计等;
LabVIEW 测试与程序优化:通过串口获取芯片发送的 ADC 数据,提高响应速度、减小延迟;
使用串口中断方案实现 ADC 电压和温度数据的采集,以及相应的 LabVIEW 上位机设计。
工程调试
通过串口打印的方式输出 ADC 电压和温度数据,包括流程图、关键代码、效果演示等。
流程图

代码
#include "main.h"
#include <stdio.h>
#define DEBUG_USART USART1
#define ADC_DR_ADDR ((uint32_t)ADC1_BASE + 0x4C)
__IO uint16_t DMA_ADCConvertedValue = 0;
void ADC_Init(void);
void Delay(uint32_t count);
int main(void)
{
/* ADC convert to voltage */
float voltage = 0;
float temperature = 0.0;
USART_Config_T USART_ConfigStruct;
/* USART config */
USART_ConfigStructInit(&USART_ConfigStruct);
USART_ConfigStruct.baudRate = 115200;
USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_ConfigStruct.mode = USART_MODE_TX;
USART_ConfigStruct.parity = USART_PARITY_NONE;
USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
BOARD_COM_Config(COM1, &USART_ConfigStruct);
/* ADC1 initialization */
ADC_Init();
/* Infinite loop */
while (1)
{
if (DMA_ReadStatusFlag(DMA1_FLAG_TC1) == SET)
{
voltage = (double)DMA_ADCConvertedValue / 4095 * 3.3;
temperature = (voltage - 1.4822f) / 0.0024f + 28U;
printf("\r\n");
printf("ADC register data = 0x%04X \r\n", DMA_ADCConvertedValue);
printf("voltage = %.03f V \r\n", voltage);
printf("Temperature = %f Celsius \r\n", temperature);
Delay(2000);
DMA_ClearStatusFlag(DMA1_FLAG_TC1);
}
}
}
void DMA_Init(void)
{
DMA_Config_T DMA_ConfigStruct;
/* Enable DMA Clock */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
/* DMA config */
DMA_ConfigStructInit(&DMA_ConfigStruct);
DMA_ConfigStruct.peripheralBaseAddr = ADC_DR_ADDR;
DMA_ConfigStruct.memoryBaseAddr = (uint32_t)&DMA_ADCConvertedValue;
DMA_ConfigStruct.dir = DMA_DIR_PERIPHERAL_SRC;
DMA_ConfigStruct.bufferSize = 1;
DMA_ConfigStruct.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
DMA_ConfigStruct.memoryInc = DMA_MEMORY_INC_ENABLE;
DMA_ConfigStruct.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
DMA_ConfigStruct.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
DMA_ConfigStruct.loopMode = DMA_MODE_CIRCULAR;
DMA_ConfigStruct.priority = DMA_PRIORITY_HIGH;
DMA_ConfigStruct.M2M = DMA_M2MEN_DISABLE;
/* Enable DMA channel */
DMA_Config(DMA1_Channel1, &DMA_ConfigStruct);
/* Enable DMA */
DMA_Enable(DMA1_Channel1);
}
void ADC_Init(void)
{
ADC_Config_T ADC_ConfigStruct;
/* Enable ADC clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
/* ADC configuration */
ADC_Reset(ADC1);
ADC_ConfigStructInit(&ADC_ConfigStruct);
ADC_ConfigStruct.mode = ADC_MODE_INDEPENDENT;
ADC_ConfigStruct.scanConvMode = DISABLE;
ADC_ConfigStruct.continuousConvMode = ENABLE;
ADC_ConfigStruct.externalTrigConv = ADC_EXT_TRIG_CONV_NONE;
ADC_ConfigStruct.dataAlign = ADC_DATA_ALIGN_RIGHT;
/* channel number */
ADC_ConfigStruct.nbrOfChannel = 1;
ADC_Config(ADC1, &ADC_ConfigStruct);
/* ADCCLK = PCLK2/6 */
RCM_ConfigADCCLK(RCM_PCLK2_DIV_6);
/* ADC channel Convert configuration */
ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_16, 1, ADC_SAMPLETIME_239CYCLES5);
ADC_EnableTempSensorVrefint(ADC1);
/* Config DMA */
DMA_Init();
/* Enable ADC DMA */
ADC_EnableDMA(ADC1);
/* Enable ADC */
ADC_Enable(ADC1);
/* Enable ADC1 reset calibration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while (ADC_ReadResetCalibrationStatus(ADC1));
/* Start ADC1 calibration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while (ADC_ReadCalibrationStartFlag(ADC1));
/* Start ADC1 Software Conversion */
ADC_EnableSoftwareStartConv(ADC1);
}
void Delay(uint32_t count)
{
uint16_t i = 0;
while (count--)
{
i = 7995;
while (i--);
}
}
#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
int fputc(int ch, FILE* f)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, (uint8_t)ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return (ch);
}
#elif defined (__GNUC__)
int __io_putchar(int ch)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return ch;
}
int _write(int file, char* ptr, int len)
{
UNUSED(file);
int i;
for (i = 0; i < len; i++)
{
__io_putchar(*ptr++);
}
return len;
}
#else
#warning Not supported compiler type
#endif
保存并编译代码,上传代码至芯片;
短按 RESET 键,复位并运行程序。
效果

串口中断
为了便于后面通过 LabVIEW 进行精确的自动化数据采集,避免串口连续发送可能引起的延迟和数据遗漏等影响,改进代码并使能串口中断,通过上位机发送指令获取 ADC 电压和温度。
包括流程图、关键代码、效果演示等。
流程图

代码
在上述 Demo 例程的基础上进行修改,使能串口中断,代码如下
#include "main.h"
#include <stdio.h>
#define DEBUG_USART USART1
#define ADC_DR_ADDR ((uint32_t)ADC1_BASE + 0x4C)
/* Buffer for received commands */
#define CMD_BUFFER_SIZE 3
uint8_t cmdBuffer[CMD_BUFFER_SIZE];
uint8_t cmdIndex = 0;
/* DMA value from ADC*/
__IO uint16_t DMA_ADCConvertedValue = 0;
/* Private function prototypes ********************************************/
void ADC_Init(void);
void Delay(uint32_t count);
void ProcessCommand(uint8_t command);
int main(void)
{
/* ADC convert to voltage */
float voltage = 0;
float temperature = 0.0;
USART_Config_T USART_ConfigStruct;
/* USART config */
USART_ConfigStructInit(&USART_ConfigStruct);
USART_ConfigStruct.baudRate = 115200;
USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_ConfigStruct.mode = USART_MODE_TX_RX;
USART_ConfigStruct.parity = USART_PARITY_NONE;
USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
BOARD_COM_Config(COM1, &USART_ConfigStruct);
/* ADC1 initialization */
ADC_Init();
/* Enable USART receive interrupt */
USART_EnableInterrupt(DEBUG_USART, USART_INT_RXBNE);
NVIC_EnableIRQ(USART1_IRQn);
/* Infinite loop */
while (1)
{
if (DMA_ReadStatusFlag(DMA1_FLAG_TC1) == SET)
{
voltage = (double)DMA_ADCConvertedValue / 4095 * 3.3;
temperature = (voltage - 1.4822f) / 0.0024f + 28U;
DMA_ClearStatusFlag(DMA1_FLAG_TC1);
}
}
}
/* USART1 interrupt handler */
void USART1_IRQHandler(void)
{
if (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_RXBNE) != RESET)
{
uint8_t data = USART_RxData(DEBUG_USART);
/* Check for command start */
if (cmdIndex == 0 && data == 0x55)
{
cmdBuffer[cmdIndex++] = data;
}
else if (cmdIndex == 1 && data == 0xAA)
{
cmdBuffer[cmdIndex++] = data;
}
else if (cmdIndex == 2)
{
cmdBuffer[cmdIndex++] = data;
/* Full command received */
if (cmdBuffer[0] == 0x55 && cmdBuffer[1] == 0xAA)
{
ProcessCommand(cmdBuffer[2]);
}
cmdIndex = 0; // Reset for next command
}
else
{
cmdIndex = 0; // Reset if sequence is broken
}
}
}
void ProcessCommand(uint8_t command)
{
float voltage = (double)DMA_ADCConvertedValue / 4095 * 3.3;
float temperature = (voltage - 1.4822f) / 0.0024f + 28U;
switch(command)
{
case 0x10: // Send voltage data
printf("Voltage: %.3f V\r\n", voltage);
break;
case 0x11: // Send temperature data
printf("Temperature: %.2f C\r\n", temperature);
break;
default:
printf("Unknown command: 0x%02X\r\n", command);
break;
}
}
/* Keep the existing DMA_Init(), ADC_Init(), Delay(), and fputc() functions unchanged */
保存并编译代码,上传代码至芯片;
短按 RESET 键,复位并运行程序。
效果

LabVIEW 上位机
基于上述串口中断查询 ADC 电压和温度的方案,设计了对应的 LabVIEW 上位机程序,便于自动化数据采集。
包括前面板设计、程序框图设计、使用方法、运行效果、数据保存等。
前面板
前面板设计包括串口配置、ADC 数值和电压的表盘显示、演化曲线图、控制按钮、数据保存配置等模块。

程序框图

使用方法
- 串口配置:端口号、波特率等;
- 单击运行按钮,设置文件保存路径;
- 点击 START 按钮,开始连续采集数据;
- 采集完成后,点击 STOP 按钮结束程序,数据自动保存至设定路径文件;
- 再次点击 START 重新采集数据;
- 点击 Terminate 按钮终止上位机程序。
运行效果

数据保存
点击 Stop 按钮,停止数据采集,文件自动保存至预设路径,3 列数据依次为 日期-时间-数值

总结
本文介绍了极海 APM32F402R Micro-EVB 开发板通过 LabVIEW 上位机实现 ADC 电压和温度数据采集的项目设计,采用串口发送和串口中断查询两种方案,包括项目介绍、工程调试、串口中断测试、LabVIEW 上位机设计、程序测试及数据保存等,为 APM32 系列产品的开发设计和工业科研等领域的应用提供了参考。