cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F103 ADC: accidental trigger if CR2 value does not change after a write

GuilhermeSschultz
Associate

I'm writing a small HAL of my own for the STM32F1 and I noticed that sometimes the DMA would read in the wrong order for seemingly no reason.

After several tests, I came up with this code:

const std = @import("std");
const microzig = @import("microzig");

const RCC = microzig.chip.peripherals.RCC;
const DMA = microzig.chip.peripherals.DMA1;
const DMA_t = microzig.chip.types.peripherals.bdma_v1;
const stm32 = microzig.hal;
const timer = microzig.hal.timer.GPTimer.init(.TIM2);

const uart = stm32.uart.UART.init(.USART1);
const gpio = stm32.gpio;
const TX = gpio.Pin.from_port(.A, 9);

pub const microzig_options = microzig.Options{
    .logFn = stm32.uart.log,
};

const AdvancedADC = microzig.hal.adc.AdvancedADC;
const ADC_pin1 = gpio.Pin.from_port(.A, 1);
const ADC_pin2 = gpio.Pin.from_port(.A, 2);

fn DMA_init(arr_addr: u32, adc_addr: u32) void {
    const CH1: *volatile DMA_t.CH = @ptrCast(&DMA.CH);
    CH1.CR.raw = 0; //disable channel
    CH1.NDTR.raw = 0;
    CH1.CR.modify(.{
        .DIR = DMA_t.DIR.FromPeripheral,
        .CIRC = 0, //disable circular mode
        .PL = DMA_t.PL.High, //high priority
        .MSIZE = DMA_t.SIZE.Bits16,
        .PSIZE = DMA_t.SIZE.Bits16,
        .MINC = 1, //memory increment mode
        .PINC = 0, //peripheral not incremented
    });

    CH1.NDTR.modify(.{ .NDT = 4 }); //number of data to transfer, 4 samples
    CH1.PAR = adc_addr; //peripheral address
    CH1.MAR = arr_addr; //memory address
    CH1.CR.modify(.{ .EN = 1 }); //enable channel
}

pub fn main() !void {
    RCC.AHBENR.modify(.{
        .DMA1EN = 1,
    });
    RCC.APB1ENR.modify(.{
        .TIM2EN = 1,
    });
    RCC.APB2ENR.modify(.{
        .AFIOEN = 1,
        .USART1EN = 1,
        .GPIOAEN = 1,
        .ADC1EN = 1,
    });
    const counter = timer.into_counter(8_000_000);
    const adc = AdvancedADC.init(.ADC1);

    const adc_data_addr: u32 = @intFromPtr(&adc.regs.DR);
    var adc_buf: [10]u16 = .{1234} ** 10;
    const adc_buf_addr: u32 = @intFromPtr(&adc_buf);

    DMA_init(adc_buf_addr, adc_data_addr);

    TX.set_output_mode(.alternate_function_push_pull, .max_50MHz);
    ADC_pin1.set_input_mode(.analog);
    ADC_pin2.set_input_mode(.analog);

    uart.apply(.{
        .baud_rate = 115200,
        .clock_speed = 8_000_000,
    });

    stm32.uart.init_logger(&uart);

    //Force disable ADC before any config
    adc.regs.SR.raw = 0;
    adc.regs.CR1.raw = 0;
    adc.regs.CR2.raw = 0;
    counter.sleep_ms(100);

    //enable ADC and temp sensor
    adc.regs.CR2.raw = 1 | (u32, 1) << 23;
    counter.sleep_ms(100);

    adc.load_sequence(&.{ 16, 17, 1, 2 });

    adc.regs.CR1.raw |= (u32, 1) << 8; //enable SCAN (bit 8)

    adc.regs.CR2.raw |= (@as(u32, 1) << 8 | (u32, 1) << 20); //enable DMA (bit 8) and EXTERNAL trigger (bit20)

    counter.sleep_ms(100); //just a dummy sleep

    adc.regs.CR1.raw |= 0; //load nothing

    counter.sleep_ms(100); //just a dummy sleep

    //=========TEST===============

    //adc.regs.CR2.raw |= 0b10; //enable CONT (bit 2) //register value changes, does not start a conversion
    //adc.regs.CR2.raw |= 0; //load nothing, value is the same, starts a ADC conversion
    //adc.regs.CR2.raw |= (@as(u32, 1) << 8 | (u32, 1) << 20); //load the same value as before, value keep the same, start a ADC conversion

    //just read the CR2, does not start a conversion
    //const foo = adc.regs.CR2.raw;
    //std.mem.doNotOptimizeAway(foo);

    counter.sleep_ms(1000);

    std.log.info("arr1: {d:0>4}", .{adc_buf[0]});
    std.log.info("arr2: {d:0>4}", .{adc_buf[1]});
    std.log.info("arr3: {d:0>4}", .{adc_buf[2]});
    std.log.info("arr4: {d:0>4}", .{adc_buf[3]});

    while (true) {}
}

(it's a Zig code made with microzig, if you want to run it just take this example and replace it with this code)



with this I noticed that if a write happens on CR2, but the value remains the same, this is interpreted as ADON activating 2x starting a conversion

it's a super simple fix in code, but I couldn't find any source citing it directly, which took a good few hours of debugging

is there any other obscure behavior in the ADC in these "old" STM32s?

0 REPLIES 0