ADC1 User Description

System Trainer의 공통 PA1/CURRENT_SENSE 경로, 독립 PB0/ADC1_IN8 NTC 분압 경로, Feedback을 ANGLE로 선택했을 때 사용하는 PA6/ADC1_IN6 AS5600 절대각 경로를 설명합니다. UART 입력은 PA10/USART1_RX의 별도 입력 경로입니다.

1. 그림에서 먼저 보기

Figure 1의 번호는 아래 표의 같은 번호 행과 연결됩니다. PA1/ADC1_IN1 전류는 TIM2_CC2 호환 경로로 측정하고, 독립 NTC 입력 PB0/ADC1_IN8은 SWSTART로 측정합니다. Feedback=POSITION이면 같은 regular slot을 PA6/ADC1_IN6와 번갈아 선택합니다. PA10/USART1_RX는 이 3.3 V ADC 경로와 전기적·논리적으로 분리된 UART 입력입니다.

ADC regular conversion path where ADC_SQR3 controls the Analog MUX, with numbered callouts 1 2 3 4 5

Figure 1. ADC regular channel, TIM2 trigger, EOC, and ADC1_2 interrupt path

Source reconstruction: RM0008 ADC regular-group structure with the current implementation TIM2_CC2/IRQ overlay.

2. TIM2 event와 ADC interrupt timing

Figure 2는 Figure 1의 4번과 5번이 시간 순서로 어떻게 이어지는지 보여줍니다. CCR2 compare는 TIM2 CPU interrupt가 아니라 ADC regular conversion trigger입니다.

TIM2 CC2 event to ADC EOC and ADC1_2 interrupt timing

Figure 2. TIM2_CC2 → conversion → EOC → ADC1_2 IRQ → DR read

Current implementation event contract. ADC completion occurs in the same simulator tick; ADC_DR read clears EOC.

3. 블록 → 레지스터 비트

읽는 순서: 지원되는 항목을 먼저 확인하고, 미지원 항목은 문서 하단의 지원하지 않는 항목에서 확인합니다.

레지스터와 필드 / bit 열은 공식 RM0008 표기만 사용합니다. 현재 코드 값, PA1/CURRENT_SENSE 경로, 같은 tick 완료 같은 현재 구현의 단순화는 설명 칸에서 분리해 연결합니다.

그림 블록레지스터필드 / bit그림에서 하는 일 / 값 의미지원 여부
1Figure 1 PA1 → GPIO analog input 경로
GPIOA clock gateRCC_APB2ENRIOPAEN[2]그림 밖에서: GPIOA register block과 PA1 mode 설정 경로에 PCLK2를 공급합니다.0 = GPIOA clock 차단1 = GPIOA clock 공급ADC1_IN1 입력이 동작하려면 IOPAEN=1, ADC1EN=1, PA1 analog mode가 모두 필요합니다.지원됨
ADC1 clock gateRCC_APB2ENRADC1EN[9]그림 밖에서: ADC1 register block과 regular conversion 경로에 PCLK2를 공급합니다.0 = ADC1 clock 차단1 = ADC1 clock 공급이 gate는 ADC 경로를 활성화하지만 PA1의 GPIO mode나 regular channel을 대신 선택하지 않습니다.지원됨
PA1 analog modeGPIOA_CRLCNF1[7:6] / MODE1[5:4]그림에서: PA1 pad의 digital input/output driver를 끄고 ADC1_IN1 analog 경로를 엽니다.MODE1 = 00 input modeCNF1 = 00 analog inputCRL[7:4] = 0x0 PA1에 쓰는 완성된 4-bit 설정값다른 MODE/CNF 조합은 GPIO User Description의 공통 decoder 표에서 확인합니다.지원됨
2Figure 1 ADC_SQR3 → Analog MUX 채널 선택
Regular sequence slot 1ADC_SQR3SQ1[4:0]그림에서: regular sequence의 첫 번째 변환 channel을 지정해 Analog MUX가 해당 입력을 Sample & Hold로 전달하게 합니다.00000..10001 = ADC channel 0..1700001 = PA1 / ADC1_IN1 CURRENT_SENSE00110 = PA6 / ADC1_IN6 AS5600 POSITION01000 = PB0 / ADC1_IN8 TEMPERATURE_SENSE01001 = 공식 STM32 PB1 / ADC1_IN9 (현재 하네스 미연결)10010..11111 = 예약값, 사용하지 않음공통 펌웨어는 PB0 NTC에 SQ1=8을 사용하고, Feedback=POSITION이면 PA6 위치 SQ1=6과 번갈아 SWSTART 단발 변환합니다. PA1 전류는 기존 DR_CH1 호환 경로를 유지합니다.지원됨
3Figure 1 12-bit conversion → EOC / ADC_DR 갱신
Conversion completion boundaryADC_SR / ADC_DREOC[1] / DATA[15:0]그림에서: 12-bit conversion block이 현재 SQ1으로 선택된 PA1·PA6·PB0 sample을 ADC_DR에 기록하고 완료 상태를 ADC_SR.EOC로 넘깁니다.EOC 0 = 변환 미완료EOC 1 = 변환 완료DATA = 0..4095 우측 정렬된 12-bit 결과공식 DATA 필드는 bits 15:0입니다. 현재 구현은 bits 11:0에 결과를 저장합니다. PA1/TIM2_CC2 결과는 trigger를 받은 같은 simulator tick에 완료되고, PB0·PA6 SWSTART는 요청당 단발 변환으로 완료됩니다. EOC와 DATA의 owning read/status 동작은 5번에서 분리해 확인합니다.지원됨
4Figure 1 / 2 TIM2_CC2 event → ADC regular trigger
TIM2 clock gateRCC_APB1ENRTIM2EN[0]그림 밖에서: TIM2 counter에 peripheral clock을 공급해 compare event를 만들 수 있게 합니다.0 = TIM2 clock 차단, counter 정지1 = TIM2 clock 공급TIM2EN=0이면 CEN이 1이어도 현재 구현의 TIM2_CNT와 TIM2_CC2 event가 진행되지 않습니다.지원됨
Counter / compareTIM2_CR1 / TIM2_CCR2CEN[0] / CCR2[15:0]그림에서: counter를 진행시키고 CCR2 compare 지점에서 TIM2_CC2 event를 만듭니다.CEN 0 = counter 정지CEN 1 = counter 시작CCR2 = 0..65535 compare count현재 구현은 한 simulator tick 동안 CNT가 CCR2에 도달하거나 지나면 event를 기록합니다. 이 event는 TIM2 CPU interrupt가 아니라 ADC trigger입니다.지원됨
ADC power / enableADC_CR2ADON[0]그림 밖에서: ADC를 power-down 상태에서 활성화해 regular conversion 경로가 동작할 수 있게 합니다.0 = ADC 비활성화 / power-down1 = ADC 활성화실제 STM32F1은 처음 ADON=1 뒤 안정화·calibration 과정이 필요합니다. 현재 Trainer는 안정화·calibration과 ADON 재쓰기 시작을 모델링하지 않으며, ADON=1은 TIM2_CC2 전류 변환과 PB0 NTC·선택형 PA6 ANGLE SWSTART 변환의 공통 실행 gate입니다.지원됨
Regular trigger selectADC_CR2EXTSEL[19:17] / EXTTRIG[20] / SWSTART[22]그림에서: regular conversion을 시작할 event를 선택하고 trigger 입력을 허용합니다.EXTSEL 000 = TIM1_CC1EXTSEL 001 = TIM1_CC2EXTSEL 010 = TIM1_CC3EXTSEL 011 = TIM2_CC2EXTSEL 100 = TIM3_TRGOEXTSEL 101 = TIM4_CC4EXTSEL 110 = EXTI11 / TIM8_TRGOEXTSEL 111 = SWSTARTEXTTRIG 0 = trigger 비활성화EXTTRIG 1 = trigger 활성화SWSTART 0 = software 시작 요청 없음SWSTART 1 = 선택한 regular channel 변환 1회 시작PA1 전류 경로는 EXTSEL=011/TIM2_CC2를 사용합니다. NTC·각도 경로는 EXTSEL=111로 PB0/SQ1=8과 선택형 PA6/SQ1=6을 단발 변환합니다. Trainer는 시작 요청 뒤 SWSTART를 0으로 clear합니다.지원됨
5Figure 1 / 2 EOC → ADC1_2 IRQ → ADC_DR read
Conversion complete statusADC_SREOC[1]그림에서: regular conversion 결과가 준비되었는지 알려 주는 하드웨어 상태 bit입니다.0 = 변환 미완료1 = 변환 완료, ADC_DR 결과 읽기 가능실제 STM32와 현재 구현 모두 software가 결과 상태를 읽는 bit입니다. 현재 구현에서는 ADC_DR read가 EOC를 clear합니다.지원됨
EOC interrupt gateADC_CR1EOCIE[5]그림에서: EOC[1] / EOCIE[5] checkpoint가 완료 상태를 ADC1_2 interrupt request로 넘길지 결정합니다.0 = EOC interrupt 비활성화1 = EOC interrupt 활성화현재 Trainer에서는 EOC=1과 EOCIE=1이면 ADC1_2 handler를 실행합니다.지원됨
Regular result readADC_DRDATA[15:0]그림에서: regular conversion의 data register를 읽습니다.0..4095 = 우측 정렬된 12-bit PA1/IN1, PA6/IN6 또는 PB0/IN8 결과공식 field 범위는 DATA[15:0]이고 현재 구현 결과는 bits 11:0에 저장됩니다. ADC_DR read는 EOC를 clear합니다. 공통 예제는 읽기 전에 기록한 SQ1 channel로 PB0 NTC cache와 PA6 각도 cache를 구분합니다.지원됨

4. 데이터시트 필드 ↔ 현재 코드

현재 코드 / 값데이터시트 필드로 해석화면에서 확인
RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_ADC1ENIOPAEN[2] = 1, IOPBEN[3] = 1, ADC1EN[9] = 1: PA1·PA6와 PB0 analog pad 및 ADC1 register 경로를 엽니다.Registers > Clock에서 GPIOA/GPIOB/ADC1 gate ON
GPIOA->CRL &= ~(0xFu << 4)GPIOA_CRL.CNF1[7:6] = 00, MODE1[5:4] = 00; 해당 pin 설정 nibble은 CRL[7:4] = 0x0Board > Lab pin routes에서 PA1 analog ADC path
ADC_SQR3_SQ1_ADC1_IN1ADC_SQR3.SQ1[4:0] = 00001Registers > ADC_SQR3에서 SQ1 1
GPIOB->CRL &= ~0xFu
ADC1->SQR3 = ADC_SQR3_SQ1_ADC1_IN8
PB0 = analog input; SQ1=8 → SWSTART → EOC → ADC_DR로 독립 NTC 분압 노드를 읽습니다.Board PB0/TEMPERATURE_SENSE, Registers ADC_SQR3·ADC_SR·ADC_DR, System의 NTC temperature
GPIOA->CRL &= ~(0xFu << 24)
ADC1->SQR3 = 6
ADC1->DR
PA6 = analog input; Feedback=POSITION일 때 SQ1=6 → SWSTART → EOC → ADC_DR로 절대각을 읽습니다.Board PA6/POSITION_IN, Registers ADC_SQR3·ADC_SR·ADC_DR, System의 Feedback
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EXTSEL_SWSTART | ADC_CR2_EXTTRIGADC_CR2.ADON[0] = 1: ADC를 활성화하고 PB0 NTC·선택형 PA6 ANGLE SWSTART 변환의 실행 gate를 엽니다.Registers > ADC_CR2에서 ADON bit 0 확인
ADC_CR2_EXTSEL_SWSTART | ADC_CR2_EXTTRIG
ADC1->CR2 |= ADC_CR2_SWSTART
EXTSEL[19:17] = 111, EXTTRIG[20] = 1, SWSTART[22] = 1: software 시작 경로를 선택·허용한 뒤 실제 regular conversion을 한 번 시작합니다.Registers > ADC_CR2와 Docs / Log의 SQ1 → SWSTART → EOC 순서
CURRENT: ADC1->CR2 = ADC_CR2_EXTSEL_TIM2_CC2 | ADC_CR2_EXTTRIG
ADC1->CR2 |= ADC_CR2_ADON
ADON[0] = 1, EXTSEL[19:17] = 011, EXTTRIG[20] = 1: 공통 PA1 전류 ADC를 활성화하고 TIM2_CC2 event 시작 경로를 허용합니다.Registers > ADC_CR2, Graph의 TIM2_CC2 marker, Docs / Log의 ADC sample event
ADC1_2_IRQHandlerADC_SR.EOC[1], ADC_CR1.EOCIE[5], ADC_DR.DATA[15:0]
현재 구현 result는 우측 정렬된 bits 11:0입니다.
Code Watch, Registers, Docs / Log

4.1 센서·아날로그 경계

항목공식 데이터시트현재 Trainer/보드 계약Reference-only 경계
NTCLE100E3103 온도R25=10 kΩ, B25/85=3977 K, 제조사 R/T 표3.3 V → R1 10 kΩ → PB0 → NTC → GND. 25 °C에서 PB0=1.65 V, ADC raw≈2048이며 PB0 ADC와 PA10 UART는 독립입니다.NTC/R1/VDDA 허용오차, self-heating, 배선저항과 thermal lag는 화면 물리에 자동 적용되지 않습니다.
AS5600 각도3.3 V mode, OUTS=00 analog full-range output; programmable angular rangeMANG=180° programmed profile을 사용하며 0..180°에 2048 distinct DAC steps가 대응합니다. ADC_DR 자체는 12-bit 0..4095입니다.I²C programming, DIR, PWM output, magnet detect/diagnostic은 현재 실행 코드에서 다루지 않습니다.
0.05 Ω raw shuntSTM32 ADC 입력은 0..VREF+ 범위여야 합니다.VPA1=I×0.05 Ω의 단방향 low-side nominal 모델입니다.실물은 Kelvin 배선, I²R 전력(5 A=1.25 W, 8.5 A=3.61 W), ADC RC/clamp 보호와 ground lift/재순환 전류 오차를 별도로 검토해야 합니다.

5. 현재 Trainer 실행 예제

아래 예제는 실제 통합 하네스처럼 NTC 분압 노드를 PB0/ADC1_IN8에서 항상 읽고, Feedback=POSITION일 때만 PA6/ADC1_IN6을 다음 SQ1으로 선택해 두 채널을 교대 SWSTART합니다. PA10/USART1_RX UART는 별도 입력이므로 이 ADC 예제에 포함하거나 온도값 전달에 사용하지 않습니다.

#include "training_api.h"

#define SENSOR_ADC_NTC_CHANNEL 8u
#define SENSOR_ADC_POSITION_CHANNEL 6u

static uint32_t sensor_adc_pending_channel = SENSOR_ADC_NTC_CHANNEL;
static uint32_t ntc_adc_raw = 0u;
static uint32_t ntc_temperature_c_x10 = 0u;
static uint32_t pa6_position_raw = 0u;

/* NTCLE100E3103, R1=10 kΩ, 0..125 °C in 5 °C steps. */
static const uint32_t ntc_adc_raw_by_5c[26] = {
    3133u, 2936u, 2724u, 2501u, 2274u, 2048u, 1827u, 1618u, 1424u,
    1246u, 1085u, 942u, 816u, 706u, 611u, 528u, 457u, 396u, 343u,
    298u, 260u, 227u, 198u, 174u, 152u, 134u
};

static uint32_t ntc_temperature_c_x10_from_raw(uint32_t raw)
{
    uint32_t index;
    if (raw >= ntc_adc_raw_by_5c[0]) return 0u;
    if (raw <= ntc_adc_raw_by_5c[25]) return 1250u;
    for (index = 0u; index < 25u; index += 1u)
    {
        uint32_t high_raw = ntc_adc_raw_by_5c[index];
        uint32_t low_raw = ntc_adc_raw_by_5c[index + 1u];
        if (raw <= high_raw && raw >= low_raw)
        {
            uint32_t span = high_raw - low_raw;
            uint32_t offset = high_raw - raw;
            return index * 50u + (offset * 50u + span / 2u) / span;
        }
    }
    return 1250u;
}

static uint32_t feedback_is_position(void)
{
    return TRAINER->FEEDBACK_MODE == TRAINER_FEEDBACK_ANGLE;
}

static void gpio_init(void)
{
    GPIOB->CRL &= ~0xFu; /* PB0 / ADC1_IN8 NTC divider node */
    if (feedback_is_position())
    {
        GPIOA->CRL &= ~(0xFu << 24); /* PA6 / ADC1_IN6 angle analog input */
    }
}

static void sensor_adc_start(uint32_t channel)
{
    sensor_adc_pending_channel = channel;
    if (channel == SENSOR_ADC_NTC_CHANNEL) {
        ADC1->SQR3 = ADC_SQR3_SQ1_ADC1_IN8;
    } else {
        ADC1->SQR3 = ADC_SQR3_SQ1_ADC1_IN6;
    }
    ADC1->CR2 |= ADC_CR2_SWSTART; /* 선택한 regular channel 단발 변환 */
}

static void adc_init(void)
{
    ADC1->CR = ADC1_CR_CH1EN; /* PA1 current legacy compatibility path */
    ADC1->SQR1 = 0u;
    ADC1->SQR2 = 0u;
    ADC1->CR1 = 0u; /* EOC polling */
    ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EXTSEL_SWSTART | ADC_CR2_EXTTRIG;
    sensor_adc_start(SENSOR_ADC_NTC_CHANNEL);
}

static void sensor_adc_service(void)
{
    uint32_t raw;
    if ((ADC1->SR & ADC_SR_EOC) == 0u) return;
    raw = ADC1->DR; /* result read; Trainer에서 EOC clear */
    if (sensor_adc_pending_channel == SENSOR_ADC_NTC_CHANNEL)
    {
        ntc_adc_raw = raw;
        ntc_temperature_c_x10 = ntc_temperature_c_x10_from_raw(raw);
        if (feedback_is_position()) {
            sensor_adc_start(SENSOR_ADC_POSITION_CHANNEL);
        } else {
            sensor_adc_start(SENSOR_ADC_NTC_CHANNEL);
        }
    }
    else
    {
        pa6_position_raw = raw;
        sensor_adc_start(SENSOR_ADC_NTC_CHANNEL);
    }
}

void user_init(void)
{
    trainer_clock_init_72mhz();
    RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_ADC1EN;
    gpio_init();
    adc_init();
}

void user_loop(void)
{
    sensor_adc_service();
    watch_u32("PB0_NTC_ADC_RAW", ntc_adc_raw);
    watch_u32("NTC_TEMPERATURE_C_X10", ntc_temperature_c_x10);
    watch_u32("PA6_ANGLE_ADC_RAW", pa6_position_raw); /* compatibility Watch name */
    watch_u32("PA1_CURRENT_ADC_RAW", ADC1->DR_CH1);
    watch_u32("ADC_SQR3", ADC1->SQR3);
}

6. 사이트에서 확인

바로 위 실행 예제로 PB0/SQ1=8 NTC 변환을 항상 확인하고, Feedback=POSITION이면 PB0와 PA6/SQ1=6이 번갈아 선택되는지 확인합니다.

7. 지원하지 않는 항목

항목공식 STM32 의미현재 구현 경계지원 여부
ADC_SMPR2.SMP1ADC1_IN1 sample cycle 선택필드 값의 저장·표시는 가능하지만 sample cycle이나 conversion latency에는 반영되지 않습니다. 현재 실행 예제에서는 사용하지 않습니다.미지원
Watchdog / injected / DMA / internal channelsfull ADC1 peripheral 기능현재 화면에 없는 기능이므로 primary figure/table/code에 넣지 않습니다.미지원

ADC_SMPR2 sample cycle, calibration, continuous/scan mode, injected conversion, DMA, analog watchdog, internal temperature sensor, VREFINT는 공식 ADC1 Datasheet에서 Reference-only로 확인합니다. 현재 Trainer에서 ADON은 TIM2_CC2 전류 측정과 PB0 NTC·선택형 PA6 ANGLE SWSTART 경로의 공통 실행 gate입니다.