要被STM32的SPI从机DMA玩死了,偶尔会跳字节,大家帮忙看看
芯片: SMT32F103RET6。问题: 在使用SPI 做从机时,接收发送都是DMA模式。 在发送时, DMA偶尔会跳过发送缓存的第2个字节, 调试了好几天, 没有头绪,望大家帮忙看看,
代码:
1. SPI 初始化
//
// SPI PART
static void m_spi_init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDefSPI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDefDMA_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
// enable clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
// MISO
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// sckmosi nss
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_13 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// enable csinterrupt
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);
EXTI_InitStructure.EXTI_Line = EXTI_Line12;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能外部中断所在的通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
#if 1
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel4); //Set DMA registers to default values
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; //Address of peripheral the DMA must map to
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)m_spi_dma_receive_buff; //Variable to which received data will be stored
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = sizeof(m_spi_dma_receive_buff); //Buffer size
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_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure); //Initialise the DMAa
DMA_DeInit(DMA1_Channel5);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; // Address of peripheral the DMA must map to
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)m_spi_dma_send_buf; // Variable from which data will be transmitted
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = sizeof(m_spi_dma_send_buf); //Buffer size
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_Channel5, &DMA_InitStructure);
#endif
// enable transfer complete & error interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);
// enable the dma channel of receiving
DMA_Cmd(DMA1_Channel4, ENABLE); //Enable the DMA1 - Channel4
SPI_Cmd(SPI2, DISABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
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_Hard;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
// enable spi error interrupt
NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);
SPI_I2S_ClearFlag(SPI2, SPI_FLAG_MODF);
SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_ERR, ENABLE);
// enable dma receive and transmit
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
// enable spi2 device
SPI_Cmd(SPI2, ENABLE);
}
2: 发送代码
/*
* @brief: send data via the spi bus.
* @param ack: pointer to the buffer of ack data.
* @param ack_size: size of the ack buffer.
* @param buf: pointer to the data buffer that real be send.
* @param size: size of real data.
*/
void board_spi_send(uint8_t *ack, int ack_size, uint8_t *buf, int size) {
int i, index = 0;
// copy ACK data to memory
if (ack != null) {
for (i = 0; i < ack_size; i++) {
m_spi_dma_send_buf = *ack++;
}
}
// copy real data to memory
if (buf != null) {
for (i = 0; i < size; i++) {
m_spi_dma_send_buf = *buf++;
}
}
// finally, it has data to send.
if (index != 0) {
#if 1
DBG_INFO("Fill data intp DMA => ");
for (i = 0; i < index; i++) {
printf("0x%02x ", m_spi_dma_send_buf);
}
printf("\r\n");
#endif
// wait for the dma finish its work.
while ((m_spi_is_sending == true || m_spi_dma_waiting_send == true) && (DMA1_Channel5->CCR & DMA_CCR5_EN));
// disable dma channel
DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
m_spi_is_sending = false;
// use to indicate that we are waiting for sending.
m_spi_dma_waiting_send = true;
// re-config the address and size
DMA1_Channel5->CMAR = (uint32_t)m_spi_dma_send_buf;
// set the buffer size
DMA1_Channel5->CNDTR = index;
} else {
printf("nothing send to spi\r\n");
}
}
3: CS 中断
//
// interrupt handler routine for spi cs
void EXTI15_10_IRQHandler(void) {
// the master start to transfer
if (EXTI_GetITStatus(EXTI_Line12) != RESET) {
// check whether there are some data waiting for sending.
if (m_spi_dma_waiting_send == true) {
DBG_INFO("enable spi send. size = %d\r\n", DMA1_Channel5->CNDTR);
// set this variable to indicate that we are sending.
m_spi_is_sending = true;
// unmark this variable.
m_spi_dma_waiting_send = false;
// enable the transmit DMA channel.
DMA1_Channel5->CCR |= DMA_CCR5_EN;
}
EXTI_ClearITPendingBit(EXTI_Line12);
}
}
4:DMA 中断
void DMA1_Channel5_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_TC5) != RESET) {
DBG_INFO("DMA_INT=> send ack ok! remain size = %d\r\n", DMA1_Channel5->CNDTR);
// disable the dma channel
DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
m_spi_is_sending = false;
DMA_ClearITPendingBit(DMA1_IT_TC5);
}
if (DMA_GetITStatus(DMA1_IT_TE5) != RESET) {
DBG_ERROR("some error occured while sending...\r\n");
// disable the dma channel
DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR5_EN);
m_spi_is_sending = false;
DMA_ClearITPendingBit(DMA1_IT_TE5);
}
}
5: SPI 中断
void SPI2_IRQHandler(void) {
if (SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_OVR)) {
DBG_ERROR("spi2 receive overrun!\r\n");
SPI_I2S_ClearITPendingBit(SPI2, SPI_I2S_IT_OVR);
}
if (SPI_I2S_GetITStatus(SPI2, SPI_IT_CRCERR)) {
DBG_ERROR("spi2 crc error!\r\n");
SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_CRCERR);
}
if (SPI_I2S_GetITStatus(SPI2, SPI_IT_MODF)) {
DBG_ERROR("spi2 mode error!\r\n");
SPI_I2S_ClearITPendingBit(SPI2, SPI_IT_MODF);
}
}
错误现象:
DMA在发送完第一个字节数据后, 跳过了第二个, 直接发送第三个了。。。。。。
看过这个时候的DMA控制寄存器, 值为 209a 字节单位没有改, 都是 Byte。 而且在整个过程中, SPI 和 DMA的错误中断都没有触发! 希望大家帮忙分析一下。。。。
本帖最后由 feixiang20 于 2018-5-29 00:28 编辑
查看DMA配置里的缓存数据大小BufferSize是你要发送的数据个数 feixiang20 发表于 2018-5-29 00:27
查看DMA配置里的缓存数据大小BufferSize是你要发送的数据个数
在发送之前CS中断里打印出来 是预期的现在把程序用 STM32F103VCT6 100PIN测试, 已经跑了一下午没出问题了 有原理图吗?你是搭的电路,还是画的PCB板上的电路。
从你的现象来看的话(首先我猜想你的 m_spi_dma_receive_buff为2),SPI片选有没有可能,受干扰,而产生一个上升沿脉冲呢? 我的spi 从机的时候也是偶尔会出错,有点郁闷
页:
[1]