打印
[应用相关]

为什么ADC采集打开DMA中断就会HardFault??

[复制链接]
524|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wyjie|  楼主 | 2019-11-21 20:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
为什么ADC采集打开DMA中断就会HardFault??

使用特权

评论回复
沙发
heweibig| | 2019-11-21 20:43 | 只看该作者
楼主详细说说怎么回事啊,这么说太笼统了

使用特权

评论回复
板凳
wyjie|  楼主 | 2019-11-21 20:47 | 只看该作者
如果我把下面代码中的
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
这句屏蔽,就不会出现HardFault,并且ADC采集的到数据是正常的。


已经折腾了一天木有结果,,求大神指点


uint16_t ADCConvertedValue[256];
uint8_t DisplayRAM[1024];

void RCC_Configuration(void)
{
  //ADCCLK由 PCLK2(72MHz) 6分频 得到 12MHz
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  
  //启用 DMA1
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  
  //启用ADC1 GPIOA SPI1 USART1
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA |
                         RCC_APB2Periph_SPI1 | RCC_APB2Periph_USART1, ENABLE);
}

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  
  //PA1 (ADC1 Channel 1)模拟输入
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  //PA5:SPI1_SCK  PA7:SPI1_MOSI
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA,&GPIO_InitStructure);
  
  //PA4:OLED的D/C作为数据/命令选择线  PA6:OLED_RST
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6;
  GPIO_Init(GPIOA,&GPIO_InitStructure);

  //PA9:USART1 Tx 作为功能引脚并上拉输出模式
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  //PA10 USART1 Rx 作为功能引脚并是浮空输入模式
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void USART_Configuration(void)
{
  USART_InitTypeDef USART_InitStructure;

  //115200 8n1
  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  USART_Init(USART1, &USART_InitStructure);
  USART_Cmd(USART1, ENABLE);
}

//重定向 printf 函数打印信息的 putchar 函数
#ifdef __GNUC__
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
int __io_putchar(int ch)
#else
int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
{
  //等待可以发送数据
  while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  
  //串口发送字符
  USART_SendData(USART1, (uint8_t) ch);
  return ch;
}

void InitADC(void)
{
  ADC_InitTypeDef ADC_InitStructure;
  DMA_InitTypeDef DMA_InitStructure;
  NVIC_InitTypeDef NVIC_InitDef;
  
  //配置 ADC1
  ADC_DeInit(ADC1);
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  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);

  /*----------------------------------------------------------------------------
    输入信号是 20000Hz,每个周期采样 2 个点,每秒要采样 40000 点。
    点间隔 1s / 40000 = 25us,ADC时钟频率 12MHz,所以采样周期 25us / 12MHz = 300
    需要每300个 ADC 周期采样一个点,但是周期最多只能配置为 239.5
    所以采集能力最终是每秒 50000 点
  ----------------------------------------------------------------------------*/
  //ADC1 通道1配置
  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);

  //启用 ADC1 DMA
  ADC_DMACmd(ADC1, ENABLE);
  
  //启用ADC1
  ADC_Cmd(ADC1, ENABLE);
  
  //ADC1 复位校准并等待结束
  ADC_ResetCalibration(ADC1);
  while(ADC_GetResetCalibrationStatus(ADC1));

  //ADC1 开始校准并等待结束
  ADC_StartCalibration(ADC1);
  while(ADC_GetCalibrationStatus(ADC1));

  
  //配置 DMA 通道1(ADC1) IRQ 中断
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  NVIC_InitDef.NVIC_IRQChannel = DMA1_Channel1_IRQn;
  NVIC_InitDef.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitDef.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitDef.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitDef);
   
  //配置 DMA1 通道1
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCConvertedValue;
  printf("%x %x\r\n", DMA_InitStructure.DMA_PeripheralBaseAddr, DMA_InitStructure.DMA_MemoryBaseAddr);
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 256;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  
  //启用 DMA1 通道1 传输完成中断
  DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
   
  //启用 DMA1 通道1
  DMA_Cmd(DMA1_Channel1, ENABLE);
  
  
printf("ADC start conv\r\n");
  //ADC1 开始采集
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

void DMA1_Channel1_IRQHandler(void)
{
    printf("DMA1_Channel1_IRQHandler\n");
  if(DMA_GetITStatus(DMA1_IT_TC1))
  {
    //通道1传输完成,清DMA发送完成标志
    DMA_ClearFlag(DMA1_FLAG_TC1);
   
    printf("DMA1_Channel1_IRQHandler\n");
  }
}

void InitSPI(void)
{
  DMA_InitTypeDef DMA_InitStructure;
  SPI_InitTypeDef SPI_InitStructure;
  
  //配置 DMA1 通道3 负责 SPI1 发送
  DMA_DeInit(DMA1_Channel3);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DisplayRAM;
  printf("%x %x\r\n", DMA_InitStructure.DMA_PeripheralBaseAddr, DMA_InitStructure.DMA_MemoryBaseAddr);
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = sizeof(DisplayRAM);
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  
  DMA_Init(DMA1_Channel3, &DMA_InitStructure);
  
  /*----------------------------------------------------------------------------
    配置 SPI1
    主机模式
    只发送不接收
    空闲时 SCLK 高电平
    SCLK由低跳变到高时数据有效
    16 分频得到 4.5M SPI
    无CRC
  ----------------------------------------------------------------------------*/
  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL  = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 0;
  SPI_Init(SPI1,&SPI_InitStructure);
  SPI_Cmd(SPI1,ENABLE);
  
  //启用 SPI DMA 发送
  SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
}

void OledWrite(uint8_t data)
{
  //等待缓冲
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);  
  SPI_I2S_SendData(SPI1, data);
  
  //返回时SPI并没有发送完数据,因为SPI状态没有TC标志,因此需要小心处理
}

void OledCmdMode()
{
  //PA4(D/C) = 0 发送命令
  Delay_ms(1);  //等待未发送完成的数据
  GPIO_ResetBits(GPIOA, GPIO_Pin_4);  
}

void OledDataMode()
{
  //PA4(D/C) = 1 发送命令
  Delay_ms(1);  //等待未发送完成的数据
  GPIO_SetBits(GPIOA, GPIO_Pin_4);  
}

void OledRefresh()
{
  DMA_Cmd(DMA1_Channel3, DISABLE);
  
  DMA_ClearITPendingBit(DMA1_IT_GL3);           //清除所有标志
  DMA1_Channel3->CNDTR = 1024;                  //重设接收长度
  DMA1_Channel3->CMAR = (uint32_t)DisplayRAM;   //重设缓冲区
  DMA_Cmd(DMA1_Channel3, ENABLE);
  
  //等待发送完成
  while (!DMA_GetFlagStatus(DMA1_FLAG_TC3));
}


void InitOled()
{
  //复位 OLED 并保持 OLED RST 低电平 1 毫秒
  GPIO_ResetBits(GPIOA, GPIO_Pin_6);
  Delay_ms(1);
  
  //RST 拉高并保持 10 微妙后才发送命令
  GPIO_SetBits(GPIOA, GPIO_Pin_6);
  Delay_us(10);
  
  //命令模式
  OledCmdMode();
  
  //初始化序列
  OledWrite(0xAE);      //set display off
  OledWrite(0xD5);      //set display clock divide ratio/oscilator frequency
  OledWrite(0x80);
  OledWrite(0xA8);      //set multiplex ratio
  OledWrite(0x3F);
  OledWrite(0xD3);      //set display offset
  OledWrite(0x00);
  OledWrite(0x40);      //set display start line
  OledWrite(0x8D);      //set charge pump
  OledWrite(0x14);
  OledWrite(0xA1);      //set segment re-map
  OledWrite(0xC8);      //set COM output scan direction
  OledWrite(0xDA);      //set COM pins hardware configuration  
  OledWrite(0x12);
  OledWrite(0x81);      //set contrast control
  OledWrite(0x8F);
  OledWrite(0xD9);      //set pre-charge period
  OledWrite(0xF1);
  OledWrite(0xDB);      //set VCOMH deselect level
  OledWrite(0x30);
  OledWrite(0xA4);      //set entire display on/off
  OledWrite(0xA6);      //set normal/inverse display
  
  //clear screen
  //delay recommended
  Delay_ms(10);
  
  OledWrite(0xAF);      //set display on
  Delay_ms(100);        //100ms delay recommended
  
  //设置为列扫描模式
  OledWrite(0x20);      //set addressing mode
  OledWrite(0x01);      //00水平页扫 01垂直列扫 02页模式
  
  //设置页起始地址和列起始地址(仅页模式可用)
  /*
  OledWrite(0xb0);      //set page start address for page addressing mode, b0-b7 = page0-page7
  OledWrite(0x00);      //set lower 4bit column start address for page addressing mode (00h-0fh)
  OledWrite(0x10);      //set higher 4bit column start address for page addressing mode (10h~1fh)
  */
  
  //设置起始页结束页和起始列结束列(仅 水平页扫 垂直列扫 可用)
  OledWrite(0x21);      //set column start and end address
  OledWrite(0x00);      //start column 0-127
  OledWrite(0x7F);      //end column 0-127
  OledWrite(0x22);      //set page start and end address
  OledWrite(0x00);      //start page 0-7
  OledWrite(0x7F);      //end page 0-7
  
  //数据模式
  OledDataMode();
}

int main()
{
  SystemInit();
  RCC_Configuration();
  GPIO_Configuration();
  USART_Configuration();
  
  printf("InitSPI\r\n");
  InitSPI();
  
  printf("InitADC\r\n");
  InitADC();
  
  printf("InitOled\r\n");
  InitOled();
  
  while(1)
  {
    printf("%x %x\r\n",ADCConvertedValue[0], ADCConvertedValue[1]);
    memset(DisplayRAM,0xff,1);
    OledRefresh();
    Delay_ms(500);
   
    memset(DisplayRAM,0,1024);
    OledRefresh();
    Delay_ms(500);
  }
  
  while(1);
}

使用特权

评论回复
地板
jiahy| | 2019-11-21 20:52 | 只看该作者
感觉是中断进错地址了。猜的

使用特权

评论回复
5
shimx| | 2019-11-21 20:55 | 只看该作者

你的这个程序时序是怎样的 SPI发送怎么控制的

使用特权

评论回复
6
liliang9554| | 2019-11-21 20:57 | 只看该作者

那这句代码不要行吗

使用特权

评论回复
7
wyjie|  楼主 | 2019-11-21 21:01 | 只看该作者
搞定了,呵呵,犯了低级错误……结贴啦,多谢各位

使用特权

评论回复
8
木木guainv| | 2019-12-11 10:26 | 只看该作者
用普通模式不就行了吗

使用特权

评论回复
9
xiaoqizi| | 2019-12-11 10:30 | 只看该作者
楼主能说清怎么解决的吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

927

主题

12706

帖子

5

粉丝