使用环境

  • 操作系统版本:Windows10
  • 操作系统平台(x86/x64):x64
  • VsCode 版本:1.73.0
  • EIDE 插件版本:3.10.0
  • C/C++ 插件版本:v1.13.3
  • 何种编译器(keil_c51/sdcc/armcc5/armgcc/...):armcc5
  • 编译器版本(非编译问题可忽略):

描述问题

stm32f103zet6 库函数中使用 ADC + DMA 循环读取模式导致烧录失败
使用 CMSIS-DAP 仿真器
烧录配置为 OpenOcd
芯片配置 stm32f1x.cfg
接口配置 cmsis-dap.cfg

当使用 ADC + DMA 循环模式时, 第一次烧录成功且程序正确运行, 但再次烧录其他程序均出错
需要芯片复位后才能再次烧录
测试使用 USART + DMA 从内存到外设模式发送数据没有问题

具体代码

int main()
{
    uint16_t ADC_ConvertedValue = 0;

	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);	
 
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;		 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));//ADC地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)
	DMA_InitStructure.DMA_BufferSize = 1; //传输内容的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定
	DMA_InitStructure.DMA_PeripheralDataSize = 
	DMA_PeripheralDataSize_HalfWord ; //外设数据单位
	DMA_InitStructure.DMA_MemoryDataSize = 
	DMA_MemoryDataSize_HalfWord ;    //内存数据单位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular  ; //DMA模式:循环传输
	DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   //禁止内存到内存的传输
	
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //配置DMA1的4通道
	DMA_Cmd(DMA1_Channel1,ENABLE);
 
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;  //禁止扫描方式
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换模式 
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
	ADC_Init(ADC1, &ADC_InitStructure);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟,为PCLK2的8分频,即9Hz
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);//配置ADC1通道11为55.5个采样周期 
	
	ADC_DMACmd(ADC1,ENABLE);
	ADC_Cmd(ADC1,ENABLE);
 
	ADC_ResetCalibration(ADC1);//复位校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成
 
	ADC_StartCalibration(ADC1);//ADC校准
	while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//由于没有采用外部触发,所以使用软件触发ADC转换
}

错误信息

Open On-Chip Debugger 0.10.0 (2019-04-26) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
adapter speed: 10000 kHz
adapter_nsrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: JTAG Supported
Info : CMSIS-DAP: FW Version = 0254
Info : CMSIS-DAP: Serial# = 11013602442031204b353043313035203231303397969903
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1     
Info : CMSIS-DAP: Interface ready
Info : High speed (adapter_khz 10000) may be limited by adapter firmware.
Info : clock speed 10000 kHz
Info : SWD DPIDR 0x2ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : stm32f1x.cpu: external reset detected
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000144 msp: 0x20000518
** Programming Started **
auto erase enabled
Info : device id = 0x000aa414
Info : flash size = 512kbytes
wrote 4096 bytes from file d:/code/star/eide/stm32f10x_standard_learn/build/Debug/stm32f10x_standard_learn.hex in 0.162198s (24.661 KiB/s)
** Programming Finished **
** Verify Started **
Error: checksum mismatch - attempting binary compare
diff 0 address 0x080004cc. Was 0x9e instead of 0x07
diff 1 address 0x080004cd. Was 0x07 instead of 0xd1
No more differences found.
embedded:startup.tcl:469: Error: ** Verify Failed **
in procedure 'program'
in procedure 'program_error' called at file "embedded:startup.tcl", line 514
at file "embedded:startup.tcl", line 469

不清楚是什么原因,本来 openocd 问题也挺多的

似乎还是不行, 不过错误信息倒是变了
测试用单独使用单通道 ADC 也没有问题
似乎找到问题的根源了, 应该就是 DMA_DeInit(DMA1_Channel1); 导致的问题, 注释掉之后就正常了
这句是按教程写的, 但越想越奇怪, 明明是第一次使用 DMA 没必要复位, 直接注释掉结果就正常了
虽然问题算是解决了, 不过还是挺好奇背后的原因是什么, 因为这个函数就只是复位 DMA1_Channel1

错误信息

Open On-Chip Debugger 0.12.0-rc2-g7a1adfbec (2022-11-06-10:08)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: FW Version = 0254
Info : CMSIS-DAP: Serial# = 11013602442031204b353043313035203231303397969903
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1     
Info : CMSIS-DAP: Interface ready
Info : clock speed 1000 kHz
Info : SWD DPIDR 0x2ba01477
Info : [stm32f1x.cpu] Cortex-M3 r2p1 processor detected
Info : [stm32f1x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
[stm32f1x.cpu] halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000144 msp: 0x20000518
** Programming Started **
Info : device id = 0x000aa414
Info : flash size = 512 KiB
Warn : Adding extra erase range, 0x08000ac0 .. 0x08000fff
** Programming Finished **
** Verify Started **
Error: checksum mismatch - attempting binary compare
embedded:startup.tcl:1516: Error: ** Verify Failed **
in procedure 'program'
in procedure 'program_error' called at file "embedded:startup.tcl", line 1577
at file "embedded:startup.tcl", line 1516

void DMA_DeInit

void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx)
{
  /* Check the parameters */
  assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
  
  /* Disable the selected DMAy Channelx */
  DMAy_Channelx->CCR &= (uint16_t)(~DMA_CCR1_EN);
  
  /* Reset DMAy Channelx control register */
  DMAy_Channelx->CCR  = 0;
  
  /* Reset DMAy Channelx remaining bytes register */
  DMAy_Channelx->CNDTR = 0;
  
  /* Reset DMAy Channelx peripheral address register */
  DMAy_Channelx->CPAR  = 0;
  
  /* Reset DMAy Channelx memory address register */
  DMAy_Channelx->CMAR = 0;
  
  if (DMAy_Channelx == DMA1_Channel1)
  {
    /* Reset interrupt pending bits for DMA1 Channel1 */
    DMA1->IFCR |= DMA1_Channel1_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel2)
  {
    /* Reset interrupt pending bits for DMA1 Channel2 */
    DMA1->IFCR |= DMA1_Channel2_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel3)
  {
    /* Reset interrupt pending bits for DMA1 Channel3 */
    DMA1->IFCR |= DMA1_Channel3_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel4)
  {
    /* Reset interrupt pending bits for DMA1 Channel4 */
    DMA1->IFCR |= DMA1_Channel4_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel5)
  {
    /* Reset interrupt pending bits for DMA1 Channel5 */
    DMA1->IFCR |= DMA1_Channel5_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel6)
  {
    /* Reset interrupt pending bits for DMA1 Channel6 */
    DMA1->IFCR |= DMA1_Channel6_IT_Mask;
  }
  else if (DMAy_Channelx == DMA1_Channel7)
  {
    /* Reset interrupt pending bits for DMA1 Channel7 */
    DMA1->IFCR |= DMA1_Channel7_IT_Mask;
  }
  else if (DMAy_Channelx == DMA2_Channel1)
  {
    /* Reset interrupt pending bits for DMA2 Channel1 */
    DMA2->IFCR |= DMA2_Channel1_IT_Mask;
  }
  else if (DMAy_Channelx == DMA2_Channel2)
  {
    /* Reset interrupt pending bits for DMA2 Channel2 */
    DMA2->IFCR |= DMA2_Channel2_IT_Mask;
  }
  else if (DMAy_Channelx == DMA2_Channel3)
  {
    /* Reset interrupt pending bits for DMA2 Channel3 */
    DMA2->IFCR |= DMA2_Channel3_IT_Mask;
  }
  else if (DMAy_Channelx == DMA2_Channel4)
  {
    /* Reset interrupt pending bits for DMA2 Channel4 */
    DMA2->IFCR |= DMA2_Channel4_IT_Mask;
  }
  else
  { 
    if (DMAy_Channelx == DMA2_Channel5)
    {
      /* Reset interrupt pending bits for DMA2 Channel5 */
      DMA2->IFCR |= DMA2_Channel5_IT_Mask;
    }
  }
}

    tonyddg
    好吧, 还是有问题, 只是刚才以为解决了太激动就发出来了, 非常抱歉
    冷静下来认真定位了一下出错的地方
    当 DMA_Cmd, DMA_Init, ADC_Cmd, ADC_Init, ADC_DMACmd, ADC_SoftwareStartConvCmd 当中任意一个被注释时错误均消失
    这函数功能分别是 开启 DMA 功能, 初始化 DMA, 开启 ADC 功能, 初始化 ADC, 开启 ADC, DMA 功能, 开启 ADC 软件触发, 也就是说只要 ADC + DMA 没有被正常开启就不会出错
    注释 ADC 扫描通道配置函数 ADC_RegularChannelConfig 与被读取引脚的 GPIO 的初始化, 问题仍然存在

      tonyddg
      今天早上再试了一下, 只要在再次烧录前关闭 DMA 或 ADC 或关闭ADC连续转换模式, 烧录就不会出错

      • ZHH likes this.
      a month later
      Write a Reply...