Добавлена простенькая симуляция АЦП в сканирующем режиме.
Но надо отлаживать и сравнивать с работой реального (в плане разных режимов работы, доделать прерывания/дма, флаги и так далее)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user