Добавлена простенькая симуляция АЦП в сканирующем режиме.

Но надо отлаживать и сравнивать с работой реального (в плане разных режимов работы, доделать прерывания/дма, флаги и так далее)
This commit is contained in:
2025-11-09 02:06:25 +03:00
parent 61202f2a94
commit be84043f18
18 changed files with 647 additions and 181 deletions

View File

@@ -0,0 +1,230 @@
#include "stm32_matlab_adc.h"
#include <string.h>
/////////////////////////////---SIMULINK FUNCTIONS---//////////////////////////
void Simulate_ADCs(void)
{
#ifdef USE_ADC1
ADC_Simulation(ADC1, &adc1s);
#endif
#ifdef USE_ADC2
ADC_Simulation(ADC2, &adc2s);
#endif
#ifdef USE_ADC3
ADC_Simulation(ADC3, &adc3s);
#endif
}
/////////////////////////////---ADC SIMULATION---/////////////////////////////
void ADC_Simulation(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS)
{
if (!(ADCx->CR2 & ADC_CR2_ADON)) return;
// Start conversion on SWSTART
if (ADCx->CR2 & ADC_CR2_SWSTART) {
ADC_Start_Conversion(ADCx, ADCS);
ADCx->CR2 &= ~ADC_CR2_SWSTART;
}
// Handle ongoing conversion - ïðîâåðÿåì ïî ôëàãó ñîñòîÿíèÿ
if (ADCS->conversion_time_elapsed >= 0) {
ADCS->conversion_time_elapsed += ADCS->simulation_step;
double total_time = ADC_Get_Total_Conversion_Time(ADCx, ADCS);
if (ADCS->conversion_time_elapsed >= total_time) {
ADC_Complete_Conversion(ADCx, ADCS);
// Continuous mode auto-restart
if (ADCx->CR2 & ADC_CR2_CONT) {
ADC_Start_Conversion(ADCx, ADCS);
}
}
}
}
void ADC_Start_Conversion(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS)
{
// Îïðåäåëÿåì êàíàë äëÿ êîíâåðñèè
if (ADCx->CR1 & ADC_CR1_SCAN) {
// Ðåæèì ñêàíèðîâàíèÿ
ADCS->current_rank = 0;
ADCS->current_channel = ADC_Get_Sequence_Channel(ADCx, 0);
}
else {
// Îäèíî÷íûé êàíàë
ADCS->current_channel = ADC_Get_Sequence_Channel(ADCx, 0);
}
ADCS->conversion_time_elapsed = 0;
}
void ADC_Complete_Conversion(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS)
{
// Êîíâåðòèðóåì àíàëîãîâîå çíà÷åíèå â öèôðîâîå
double analog_val = 0;
if (ADCS->channel_connected[ADCS->current_channel]) {
analog_val = ADCS->channel_values[ADCS->current_channel];
}
if (analog_val < 0)
analog_val = 0;
if (analog_val > 3.3)
analog_val = 3.3;
// Ñíà÷àëà âû÷èñëÿåì çíà÷åíèå ÀÖÏ
ADCS->last_conversion_value = (uint16_t)((analog_val / 3.3) * 4095.0);
// Ïîòîì äîáàâëÿåì øóì â LSB
int32_t noisy_value = (int32_t)ADCS->last_conversion_value + (rand() % (2 * ADC_NOISE_LSB + 1)) - ADC_NOISE_LSB;
// Ñàòóðàöèÿ öèôðîâîãî çíà÷åíèÿ
if (noisy_value < 0) noisy_value = 0;
if (noisy_value > 4095) noisy_value = 4095;
ADCS->last_conversion_value = (uint16_t)noisy_value;
// Çàïèñûâàåì â DR
ADCx->DR = ADCS->last_conversion_value;
// Óñòàíàâëèâàåì ôëàã EOC
ADCx->SR |= ADC_SR_EOC;
// Îáðàáîòêà DMA
if (ADCx->CR2 & ADC_CR2_DMA) {
ADC_DMA_Transfer(ADCx, ADCS);
}
// Îáðàáîòêà ñêàíèðîâàíèÿ
if (ADCx->CR1 & ADC_CR1_SCAN) {
ADCS->current_rank++;
uint32_t seq_len = ADC_Get_Sequence_Length(ADCx);
if (ADCS->current_rank < seq_len) {
// Ñëåäóþùèé êàíàë â ïîñëåäîâàòåëüíîñòè
ADCS->current_channel = ADC_Get_Sequence_Channel(ADCx, ADCS->current_rank);
ADCS->conversion_time_elapsed = 0;
return;
}
else {
// Êîíåö ïîñëåäîâàòåëüíîñòè
#ifdef ADC_SR_EOS
ADCx->SR |= ADC_SR_EOS;
#endif
}
}
ADCS->conversion_time_elapsed = 0;
}
/////////////////////////////---REGISTER-BASED FUNCTIONS---///////////////////
double ADC_Get_Total_Conversion_Time(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS)
{
// Ïîëó÷àåì sampling time èç ðåãèñòðîâ
uint32_t sampling_cycles = ADC_Get_Sampling_Cycles(ADCx, ADCS->current_channel);
// Conversion cycles ôèêñèðîâàíû äëÿ ðàçðåøåíèÿ
uint32_t conversion_cycles = 12; // Äëÿ 12-bit
double total_cycles = sampling_cycles + conversion_cycles;
double adc_clock = ADCS->adc_clock_freq; // ×àñòîòà øèíû
return total_cycles / adc_clock;
}
uint32_t ADC_Get_Sampling_Cycles(ADC_TypeDef* ADCx, uint32_t channel)
{
// Ïîëó÷àåì sampling time èç SMPR1/SMPR2
uint32_t smpr_code;
if (channel <= 9) {
smpr_code = (ADCx->SMPR2 >> (channel * 3)) & 0x7;
}
else {
smpr_code = (ADCx->SMPR1 >> ((channel - 10) * 3)) & 0x7;
}
// Convert SMPR code to actual cycles
static const uint32_t cycles_map[8] = { 3, 15, 28, 56, 84, 112, 144, 480 };
return (smpr_code < 8) ? cycles_map[smpr_code] : 3;
}
uint32_t ADC_Get_Sequence_Length(ADC_TypeDef* ADCx)
{
#ifdef ADC_SQR1_L
return ((ADCx->SQR1 & ADC_SQR1_L) >> ADC_SQR1_L_Pos) + 1;
#else
return ((ADCx->SQR1 & 0x00F00000) >> 20) + 1;
#endif
}
uint32_t ADC_Get_Sequence_Channel(ADC_TypeDef* ADCx, uint32_t rank)
{
if (rank > 15) return 0;
if (rank < 6) {
return (ADCx->SQR3 >> (rank * 5)) & 0x1F;
}
else if (rank < 12) {
return (ADCx->SQR2 >> ((rank - 6) * 5)) & 0x1F;
}
else {
return (ADCx->SQR1 >> ((rank - 12) * 5)) & 0x1F;
}
}
/////////////////////////////---DMA FUNCTIONS---///////////////////////////////
void ADC_DMA_Transfer(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS)
{
if (!ADCS->dma_buffer || ADCS->dma_buffer_size == 0) return;
ADCS->dma_buffer[ADCS->dma_current_index] = ADCx->DR;
ADCS->dma_current_index++;
if (ADCS->dma_current_index >= ADCS->dma_buffer_size) {
if (ADCS->dma_circular) {
ADCS->dma_current_index = 0;
}
}
}
/////////////////////////////---CHANNEL FUNCTIONS---///////////////////////////
void ADC_Set_Channel_Value(ADC_TypeDef* ADCx, uint32_t channel, double voltage)
{
#ifdef USE_ADC1
if (ADCx == ADC1 && channel < 19) {
adc1s.channel_values[channel] = voltage;
adc1s.channel_connected[channel] = 1;
}
#endif
#ifdef USE_ADC2
if (ADCx == ADC2 && channel < 19) {
adc2s.channel_values[channel] = voltage;
adc2s.channel_connected[channel] = 1;
}
#endif
#ifdef USE_ADC3
if (ADCx == ADC3 && channel < 19) {
adc3s.channel_values[channel] = voltage;
adc3s.channel_connected[channel] = 1;
}
#endif
}
/////////////////////////////---INITIALIZATION---/////////////////////////////
void ADC_SIM_DEINIT(void)
{
#ifdef USE_ADC1
memset(&adc1s, 0, sizeof(adc1s));
#endif
#ifdef USE_ADC2
memset(&adc2s, 0, sizeof(adc2s));
#endif
#ifdef USE_ADC3
memset(&adc3s, 0, sizeof(adc3s));
#endif
}
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,56 @@
#ifndef _MATLAB_ADC_H_
#define _MATLAB_ADC_H_
#include "stm32_matlab_conf.h"
#ifdef STM32F1
#define ADC_NOISE_LSB 10 // Шум в LSB (квантах АЦП)
#endif
#ifdef STM32F4
#define ADC_NOISE_LSB 2 // Шум в LSB (квантах АЦП)
#endif
/////////////////////////////---STRUCTURES---///////////////////////////
struct ADC_Sim
{
double conversion_time_elapsed;
uint32_t current_channel;
uint32_t current_rank;
uint16_t last_conversion_value;
double channel_values[19];
uint8_t channel_connected[19];
// DMA
uint32_t* dma_buffer;
uint32_t dma_buffer_size;
uint32_t dma_current_index;
uint8_t dma_circular;
// Timing
double simulation_step;
double adc_clock_freq;
};
///////////////////////////////////////////////////////////////////////
///////////////////////////---FUNCTIONS---///////////////////////////
void Simulate_ADCs(void);
void ADC_Simulation(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS);
void ADC_Start_Conversion(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS);
void ADC_Complete_Conversion(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS);
double ADC_Get_Total_Conversion_Time(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS);
uint32_t ADC_Get_Sampling_Cycles(ADC_TypeDef* ADCx, uint32_t channel);
uint32_t ADC_Get_Sequence_Length(ADC_TypeDef* ADCx);
uint32_t ADC_Get_Sequence_Channel(ADC_TypeDef* ADCx, uint32_t rank);
void ADC_DMA_Transfer(ADC_TypeDef* ADCx, struct ADC_Sim* ADCS);
void ADC_Set_Channel_Value(ADC_TypeDef* ADCx, uint32_t channel, double voltage);
void ADC_SIM_DEINIT(void);
///////////////////////////////////////////////////////////////////////
#endif // _MATLAB_ADC_H_

View File

@@ -16,11 +16,21 @@ MCU_CortexMemoryTypeDef MCU_CORTEX_MEM;
void Initialize_Periph_Sim(void)
{
Init_TIM_SIM();
Init_ADC_SIM();
}
// MCU PERIPH SIM
void Simulate_Periph_Sim(void)
{
Simulate_TIMs();
Simulate_ADCs();
}
// MCU PERIPH DEINIT
void deInitialize_Periph_Sim(void)
{
TIM_SIM_DEINIT();
ADC_SIM_DEINIT();
}
// MCU DEINIT
void deInitialize_MCU(void)
@@ -85,6 +95,7 @@ struct TIM_Sim tim14s;
void Init_TIM_SIM(void)
{
#ifdef USE_TIM1
memset(&tim1s, 0, sizeof(tim1s));
tim1s.tx_cnt = TIM1->CNT;
tim1s.tx_step = hmcu.sSimSampleTime * ABP2_TIMS_Value;
@@ -98,6 +109,7 @@ void Init_TIM_SIM(void)
tim1s.Channels.OC4_PIN_SHIFT = 11;
#endif
#ifdef USE_TIM2
memset(&tim2s, 0, sizeof(tim2s));
tim2s.tx_cnt = TIM2->CNT;
tim2s.tx_step = hmcu.sSimSampleTime * ABP1_TIMS_Value;
@@ -111,6 +123,7 @@ void Init_TIM_SIM(void)
tim2s.Channels.OC4_PIN_SHIFT = 3;
#endif
#ifdef USE_TIM3
memset(&tim3s, 0, sizeof(tim3s));
tim3s.tx_cnt = TIM3->CNT;
tim3s.tx_step = hmcu.sSimSampleTime * ABP1_TIMS_Value;
@@ -124,6 +137,7 @@ void Init_TIM_SIM(void)
tim3s.Channels.OC4_PIN_SHIFT = 9;
#endif
#ifdef USE_TIM4
memset(&tim4s, 0, sizeof(tim4s));
tim4s.tx_cnt = TIM4->CNT;
tim4s.tx_step = hmcu.sSimSampleTime * ABP1_TIMS_Value;
@@ -137,6 +151,7 @@ void Init_TIM_SIM(void)
tim4s.Channels.OC4_PIN_SHIFT = 9;
#endif
#ifdef USE_TIM5
memset(&tim5s, 0, sizeof(tim5s));
tim5s.tx_cnt = TIM5->CNT;
tim5s.tx_step = hmcu.sSimSampleTime * ABP1_TIMS_Value;
@@ -150,6 +165,7 @@ void Init_TIM_SIM(void)
tim5s.Channels.OC4_PIN_SHIFT = 3;
#endif
#ifdef USE_TIMx
memset(&tim6s, 0, sizeof(tim6s));
tim6s.tx_cnt = TIMx->CNT;
tim6s.tx_step = hmcu.sSimSampleTime * ABP1_TIMS_Value;
@@ -165,3 +181,39 @@ void Init_TIM_SIM(void)
}
/*-------------------------------TIMERS----------------------------------*/
//-----------------------------------------------------------------------//
//-----------------------------------------------------------------------//
/*---------------------------------ADC-----------------------------------*/
#ifdef USE_ADC1
struct ADC_Sim adc1s;
#endif
#ifdef USE_ADC2
struct ADC_Sim adc2s;
#endif
#ifdef USE_ADC3
struct ADC_Sim adc3s;
#endif
void Init_ADC_SIM(void)
{
#ifdef USE_ADC1
memset(&adc1s, 0, sizeof(adc1s));
adc1s.simulation_step = hmcu.sSimSampleTime;
adc1s.adc_clock_freq = ABP2_Value; // Частота APB2
#endif
#ifdef USE_ADC2
memset(&adc2s, 0, sizeof(adc2s));
adc2s.simulation_step = hmcu.sSimSampleTime;
adc2s.adc_clock_freq = ABP2_Value; // Частота APB2
#endif
#ifdef USE_ADC3
memset(&adc3s, 0, sizeof(adc3s));
adc3s.simulation_step = hmcu.sSimSampleTime;
adc3s.adc_clock_freq = ABP2_Value; // Частота APB2
#endif
}
/*---------------------------------ADC-----------------------------------*/
//-----------------------------------------------------------------------//

View File

@@ -54,13 +54,13 @@
//-----------------------------------------------------------------------//
/*------------------------------FUNCTIONS--------------------------------*/
void deInitialize_Periph_Sim(void);
void Simulate_Periph_Sim(void);
void deInitialize_MCU(void);
void Initialize_Periph_Sim(void);
#include "stm32_matlab_rcc.h"
#include "stm32_matlab_gpio.h"
//-----------------------------------------------------------------------//
/*-------------------------------TIMERS----------------------------------*/
//#if defined(USE_TIM1) || defined(USE_TIM2) || defined(USE_TIM3) || defined(USE_TIM4) || defined(USE_TIM5) || \
@@ -116,4 +116,22 @@ extern struct TIM_Sim tim14s;
/*-------------------------------TIMERS----------------------------------*/
//-----------------------------------------------------------------------//
//-----------------------------------------------------------------------//
/*---------------------------------ADC-----------------------------------*/
#include "stm32_matlab_adc.h"
#ifdef USE_ADC1
extern struct ADC_Sim adc1s;
#endif
#ifdef USE_ADC2
extern struct ADC_Sim adc2s;
#endif
#ifdef USE_ADC3
extern struct ADC_Sim adc3s;
#endif
/*---------------------------------ADC-----------------------------------*/
//-----------------------------------------------------------------------//
#endif // _MATLAB_SETUP_H_

View File

@@ -16,8 +16,7 @@
],
"PeriphSimulation": [
"uwTick = hmcu.SystemClock / (MCU_CORE_CLOCK / 1000)",
"Simulate_TIMs()",
"Simulate_GPIO_BSRR()"
"Simulate_Periph_Sim()"
],
"PeriphDeinit": [
"deInitialize_Periph_Sim()"
@@ -56,7 +55,7 @@
"Tab_ADC_Enable": {
"Prompt": "Enable ADCs",
"Type": "checkbox",
"Default": false,
"Default": true,
"NewRow": true
}
}
@@ -583,17 +582,27 @@
}
},
"ADC": {
"Sources": [
"Drivers/STM32_SIMULINK/stm32_matlab_adc.c"
],
"Defines": {
"ADC1_Enable": {
"Prompt": "ADC1 Enable",
"Def": "ADC1_ENABLE",
"Def": "USE_ADC1",
"Type": "checkbox",
"Default": false,
"Default": true,
"NewRow": true
},
"ADC2_Enable": {
"Prompt": "ADC2 Enable",
"Def": "ADC2_ENABLE",
"Def": "USE_ADC2",
"Type": "checkbox",
"Default": false,
"NewRow": true
},
"ADC3_Enable": {
"Prompt": "ADC3 Enable",
"Def": "USE_ADC3",
"Type": "checkbox",
"Default": false,
"NewRow": true

View File

@@ -197,24 +197,24 @@ static void mdlTerminate(SimStruct* S)
//// Простая версия - ждем завершения потока
// Ждем до 5 секунд
for (int i = 0; i < 50; i++) {
// Проверяем, завершился ли поток (упрощенная проверка)
DWORD exitCode;
if (GetExitCodeThread(hmcu.hMCUThread, &exitCode) && exitCode != STILL_ACTIVE) {
break; // поток завершился
}
Sleep(100); // ждем 100ms
}
//// Даем потоку шанс завершиться нормально
//DWORD waitResult = WaitForSingleObject(hmcu.hMCUThread, 3000);
//if (waitResult == WAIT_TIMEOUT) {
// // Поток не ответил - завершаем принудительно
// TerminateThread(hmcu.hMCUThread, 0);
//// Ждем до 5 секунд
//for (int i = 0; i < 50; i++) {
// // Проверяем, завершился ли поток (упрощенная проверка)
// DWORD exitCode;
// if (GetExitCodeThread(hmcu.hMCUThread, &exitCode) && exitCode != STILL_ACTIVE) {
// break; // поток завершился
// }
// Sleep(100); // ждем 100ms
//}
// Даем потоку шанс завершиться нормально
DWORD waitResult = WaitForSingleObject(hmcu.hMCUThread, 10000);
if (waitResult == WAIT_TIMEOUT) {
// Поток не ответил - завершаем принудительно
TerminateThread(hmcu.hMCUThread, 0);
}
CloseHandle(hmcu.hMCUThread);
hmcu.hMCUThread = NULL;
}

View File

@@ -114,8 +114,7 @@ void MCU_Periph_Simulation(SimStruct* S)
{
// PERIPH SIM START
uwTick = hmcu.SystemClock / (MCU_CORE_CLOCK / 1000);
Simulate_TIMs();
Simulate_GPIO_BSRR();
Simulate_Periph_Sim();
// PERIPH SIM END
}

View File

@@ -51,11 +51,11 @@
// INPUT/OUTPUTS PARAMS START
#define IN_PORT_NUMB 2
#define ADC_PORT_1_WIDTH 6
#define IN_PORT_2_WIDTH 16
#define IN_PORT_2_WIDTH 1
#define OUT_PORT_NUMB 2
#define THYR_PORT_1_WIDTH 6
#define OUT_PORT_2_WIDTH 16
#define OUT_PORT_2_WIDTH 1
// INPUT/OUTPUTS PARAMS END
/** WRAPPER_CONF

View File

@@ -13,6 +13,7 @@
//#include "stm32_matlab_conf.h"
#include "main.h"
#include "tim.h"
#include "adc.h"
// INCLUDES END
#endif //_APP_INCLUDES_H_

View File

@@ -21,6 +21,8 @@ void app_init(void) {
MX_TIM3_Init();
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_1);
MX_ADC1_Init();
HAL_ADC_Start(&hadc1);
// USER APP INIT END
}

View File

@@ -32,7 +32,7 @@ void ThyristorWrite(real_T* Buffer)
*/
void app_readInputs(const real_T* Buffer) {
// USER APP INPUT START
ADC_Set_Channel_Value(ADC1, 0, ReadInputArray(1,0));
// USER APP INPUT END
}
@@ -44,5 +44,6 @@ void app_readInputs(const real_T* Buffer) {
void app_writeOutputBuffer(real_T* Buffer) {
// USER APP OUTPUT START
ThyristorWrite(Buffer);
WriteOutputArray(ADC1->DR, 1, 0);
// USER APP OUTPUT END
}

Binary file not shown.