Jump to content

    
Sign in to follow this  
Donker

Как отправить массив по I2C через DMA на STM32F105 или F030?

Recommended Posts

Как отправить массив (uint8_t) по I2C через DMA на STM32F105RBT6 или STM32F030F4 используя CMSIS?   
Желательно сначала разобраться как это сделать на STM32F105RBT6 а потом перенести код на STM32F030F4.

Сейчас я отправляю на STM32F105RBT6 без DMA буффер OLED экрана (SSD1306 128x32) таким образом:

Спойлер

 


//--------------------------------------------------------------------------------------------
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   //включаем тактирование

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;  //6,7 I2C экрана
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //выход открытый коллектор
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);    
GPIOB->BSRR = GPIO_BSRR_BS6; //высокий уровень
GPIOB->BSRR = GPIO_BSRR_BS7; //высокий уровень
//--------------------------------------------------------------------------------------------
#define F_CPU 72000000UL

#define        NAME        0b01111000        //АДРЕС УСТРОЙСТВА НА ШИНЕ        0b01111000 = 0x78
#define        DATS        0b01000000        //ПЕРЕДАЧА НЕСКОЛЬКИХ БАЙТ ДАННЫХ 0b01000000 = 0x40
#define        DAT            0b11000000        //ПЕРЕДАЧА ОДНОГО БАЙТа ДАННЫХ    0b11000000 = 0xC0

uint8_t screen_buff[512]; //(128x32)/8
//--------------------------------------------------------------------------------------------

void I2C_init(void)
{
//////////////////////////////////////////////////////////////////////////////////////    
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //включаем тактирование I2C1

I2C1->CR2 &= ~I2C_CR2_FREQ;
I2C1->CR2 |= 36; //частота APB1 в мгц

I2C1->CCR &= ~I2C_CCR_CCR;
I2C1->CCR |= 30; //частота SCL
I2C1->CCR |= I2C_CCR_FS; //выбор режима SM Standart mode – 100КГц  или FM Fast mode - 400КГц

I2C1->TRISE = 12; //максимальное время нарастания импульса

I2C1->CR1 |= I2C_CR1_PE; //включаем I2C1
//////////////////////////////////////////////////////////////////////////////////////
}
void I2C_StartCondition(void) //СТАРТ
{
///////////////////////////////////////////////
I2C1->CR1 |= I2C_CR1_START;       
while (!(I2C1->SR1 & I2C_SR1_SB)){}
(void) I2C1->SR1;
///////////////////////////////////////////////            
}
void I2C_StopCondition(void) //СТОП
{
////////////////////////////////////////    
while (!(I2C1->SR1 & I2C_SR1_BTF)){}    
I2C1->CR1 |= I2C_CR1_STOP;
///////////////////////////////////////
}
void I2C_SendByte(unsigned char data)
{
//////////////////////////////////////////////////////////////////////////////////////        
I2C1->DR = data;
while (0 == (I2C1->SR1 & I2C_SR1_TXE)){}
//////////////////////////////////////////////////////////////////////////////////////        
}
void I2C_SendAddr(void)
{
//////////////////////////////////////////////////////////////////////////////////////        
I2C1->DR = NAME;
while (!(I2C1->SR1 & I2C_SR1_ADDR)){}
(void) I2C1->SR1;
(void) I2C1->SR2;
//////////////////////////////////////////////////////////////////////////////////////        
}
void OLED_SendScreen(void)
{
//////////////////////////////////////////////////////////////////////////////////////    
I2C_StartCondition();  
I2C_SendAddr();      //посыл адреса+0
I2C_SendByte(DATS);    
for(unsigned int i=0; i<512; i++)
  {
  I2C_SendByte(screen_buff[i]);
  }
I2C_StopCondition();
//////////////////////////////////////////////////////////////////////////////////////        
}    

//--------------------------------------------------------------------------------------------

 

как сюда прикрутить DMA?

Можно как то обойтись без функции I2C_SendAddr используя только I2C_SendByte (добавив адрес  в начало массива) что бы не ждать пока отправиться байт адреса?

 

Edited by Donker

Share this post


Link to post
Share on other sites

Сделайте отправку по прераваниям.

Или настройте прерывание по завершению отправки адреса, а в обработчике этого прерывания уже запускайте DMA на отправку данных.

И Start Condition тоже в прерывание лучше перенести, а не ждать.

Share this post


Link to post
Share on other sites

Я просто не пойму почему в версии проекта с ногодрыгом я отправлял абсолютно все байты, включая адрес, на экран одной функцией:

 

Спойлер

 


void I2C_SendByte(unsigned char data) //ПЕРЕДАЧА БАЙТА и АСК
{
//////////////////////////////////////////////////////////////////////////////////////		
for(unsigned char k=8; k>0; k--)
  {
	if(data&(1<<(k-1)))
	  {
		SCL_LOW;
		SDA_HI;
		_delay_us(1); //подобрать
		SCL_HI;
		_delay_us(1); //подобрать
		SCL_LOW;
	  }
	  else
	    {
		  SCL_LOW;
			SDA_LOW;
			_delay_us(1); //подобрать
			SCL_HI;
			_delay_us(1); //подобрать
			SCL_LOW;
	    }
}
_delay_us(1); //подобрать
//--------------ACK-------------
SDA_HI; 
_delay_us(1); //подобрать
SCL_HI;
_delay_us(1); //подобрать
//------------------------------
if(!SDA_inp) {SCL_LOW;}
_delay_us(3); //подобрать
//////////////////////////////////////////////////////////////////////////////////////		 

 

а в аппаратной версии, если попытаюсь вместо I2C_SendAddr использовать  I2C_SendByte, адрес отправляется, экран нормально реагирует,

но  происходит бесконечное зависание на этапе ожидания бита подтверждения отправки байта:

while (0 == (I2C1->SR1 & I2C_SR1_TXE)){}

Share this post


Link to post
Share on other sites

Что-то не работает моя переделка снипета от F0

Вот часть кода:

Спойлер

 


//--------------------------------------------------------------------------------------------

#define        NAME        0b01111000        //АДРЕС УСТРОЙСТВА НА ШИНЕ        0b01111000 = 0x78
#define        DATS        0b01000000        //ПЕРЕДАЧА НЕСКОЛЬКИХ БАЙТ ДАННЫХ 0b01000000 = 0x40
#define        DAT            0b11000000        //ПЕРЕДАЧА ОДНОГО БАЙТа ДАННЫХ    0b11000000 = 0xC0
#define        COM            0b10000000        //ПЕРЕДАЧА ОДНОГО БАЙТа КОМАНДЫ   0b10000000 = 0x80

uint8_t screen_buff[513]; //буффер экрана 512 байт ((128x32)/8) + самый первый байт для команды DATS

//--------------------------------------------------------------------------------------------

void DMA_init(void)
{
////////////////////////////////////////////////////////////////////////    
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* Enable the peripheral clock DMA1 */

/* DMA1 Channel4 I2C1_TX config */
/*      Memory to peripheral */                                        
/*      8-bit transfer */
DMA1_Channel4->CPAR = (uint32_t)&(I2C1->DR);         //* (1)  Peripheral address */ DMA1_Channel4->CPAR = (uint32_t)&(I2C1->TXDR);
DMA1_Channel4->CMAR = (uint32_t)screen_buff;         //* (2)  Memory address */
DMA1_Channel4->CCR |= DMA_CCR4_MINC | DMA_CCR4_DIR;  //* (3)  Memory increment */ DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR;
////////////////////////////////////////////////////////////////////////    
}

void I2C_init(void)
{
//////////////////////////////////////////////////////////////////////////////////////    
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //включаем тактирование I2C1

I2C1->CR2 &= ~I2C_CR2_FREQ;
I2C1->CR2 |= 36; //частота APB1 в мгц //36

I2C1->CCR &= ~I2C_CCR_CCR;
I2C1->CCR |= 30; //частота SCL//34//
I2C1->CCR |= I2C_CCR_FS; //выбор режима SM Standart mode – 100КГц  или FM Fast mode - 400КГц

I2C1->TRISE = 12; //максимальное время нарастания импульса //12

I2C1->CR1 |= I2C_CR1_PE; //включаем I2C1
//---------------------------------настройки для DMA--------------------------------------------------
I2C1->CR1 = I2C_CR1_PE | I2C_CR2_DMAEN;  /* (2) Periph enable */
I2C1->CR2 =  (512 << 16) | (NAME<<0); /* (3) Slave address = 0x5A, write transfer, 2 bytes to transmit, autoend */ //I2C_CR2_AUTOEND - это где брать?
//////////////////////////////////////////////////////////////////////////////////////
}

void OLED_SendScreen(void)
{
//////////////////////////////////////////////////////////////////////////////////////
//---------------------Посылка буфера экрана через без DMA(работает) -----------------    
//I2C_StartCondition();  
//I2C_SendAddr();  //I2C_SendByte(NAME);        //посыл адреса+0
//for(unsigned int i=0; i<513; i++)
//  {
//  I2C_SendByte(screen_buff[i]);
//  }
//I2C_StopCondition();     
//---------------------Посылка буфера экрана через DMA(не работает) ------------------    
    
//if((I2C1->ISR & I2C_ISR_TXE) == (I2C_ISR_TXE))  /* Check Tx empty */
 
    /* start transmission with DMA */
    DMA1_Channel4->CCR &=~ DMA_CCR4_EN;
    DMA1_Channel4->CNDTR = 513;/* Data size */
    DMA1_Channel4->CCR |= DMA_CCR4_EN;

    I2C1->CR1 |= I2C_CR1_START; /* Go */     
//////////////////////////////////////////////////////////////////////////////////////        
}

void OLED_init(void)  //ИНИЦИАЛИЗАЦИЯ ДИСПЛЕЯ
{
//////////////////////////////////////////////////////////////////////////////////////
I2C_init();    
DMA_init();        
I2C_StartCondition();
I2C_SendAddr();  
for(unsigned char k=0; k<31; k++)
    {
    I2C_SendByte(COM);
    I2C_SendByte(ini_mass[k]);
    }
I2C_StopCondition();    
screen_buff[0] =     DATS;
//////////////////////////////////////////////////////////////////////////////////////    
}
//--------------------------------------------------------------------------------------------

 


 

Если раскомментировать в функции OLED_SendScreen пересылку без DMA - тогда работает, если выполнить
OLED_SendScreen в том виде как сейчас, в ЛА видно что происходит "условие старт", после чего линии лежат
в нуле и ничего не передаётся.

Снипет смотрел этот:

 

Спойлер

 


**
  ******************************************************************************
  * @file    03_CommunicationUsingDMA/main.c
  * @author  MCD Application Team
  * @version V1.2.0
  * @date    19-June-2015
  * @brief   This code example shows how to configure the GPIOs and I2C
  *          in order to receive with slave and transmit with master.
  *
 ===============================================================================
                    #####       MCU Resources     #####
 ===============================================================================
   - RCC
   - GPIO PB6(I2C1_SCL),PB7(I2C1_SDA),PB10(I2C2_SCL),PB11(I2C2_SDA),PA0,PC8,PC9
   - I2C1 (slave), I2C2 (master)
   - EXTI
   
 ===============================================================================
                    ##### How to use this example #####
 ===============================================================================
   - this file must be inserted in a project containing  the following files :
      o system_stm32f0xx.c, startup_stm32f072xb.s
      o stm32f0xx.h to get the register definitions
      o CMSIS files


 ===============================================================================
                    ##### How to test this example #####
 ===============================================================================
   - Plug wires between PB6/PB10 and PB7/PB11, 4K7 PU are already on the board.
   - Launch the program
   - Press the user button to initiate a transmit request by master
     then slave receivess a byte
   - The green and orange LEDs toggles if everything goes well

  *    
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx.h"

/** @addtogroup STM32F0_Snippets
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define I2C1_OWN_ADDRESS (0x5A)
#define I2C_CMD_TOGGLE_ORANGE (0x81)
#define I2C_CMD_TOGGLE_GREEN (0x82)
#define SIZE_OF_DATA  (2)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t datatosend[SIZE_OF_DATA]={I2C_CMD_TOGGLE_ORANGE,I2C_CMD_TOGGLE_GREEN};
uint8_t datatoreceive[SIZE_OF_DATA];

/* Private function prototypes -----------------------------------------------*/
void Configure_GPIO_LED(void);
void Configure_GPIO_I2C1(void);
void Configure_DMA_I2C1(void);
void Configure_I2C1_Slave(void);
void Configure_GPIO_I2C2(void);
void Configure_DMA_I2C2(void);
void Configure_I2C2_Master(void);
void Configure_GPIO_Button(void);
void Configure_EXTI(void);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
  /*!< At this stage the microcontroller clock setting is already configured,
       this is done through SystemInit() function which is called from startup
       file (startup_stm32f072xb.s) before to branch to application main.
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32f0xx.c file
     */

  Configure_GPIO_LED();
  Configure_GPIO_I2C1();
  Configure_DMA_I2C1();
  Configure_I2C1_Slave();
  Configure_GPIO_I2C2();
  Configure_DMA_I2C2();
  Configure_I2C2_Master();
  Configure_GPIO_Button();
  Configure_EXTI();

  /* Initiate I2C sequence in button IRQ handler */
    
  /* Infinite loop */
  while (1)
  {

  }
}

/**
  * @brief  This function :
             - Enables GPIO clock
             - Configures the Green LED pin on GPIO PC9
             - Configures the orange LED pin on GPIO PC8
  * @param  None
  * @retval None
  */
__INLINE void Configure_GPIO_LED(void)
{
  /* Enable the peripheral clock of GPIOC */
  RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
    
  /* Select output mode (01) on PC8 and PC9 */
  GPIOC->MODER = (GPIOC->MODER & ~(GPIO_MODER_MODER8 | GPIO_MODER_MODER9)) \
                 | (GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0);
}

/**
  * @brief  This function :
             - Enables GPIO clock
             - Configures the I2C1 pins on GPIO PB6 PB7
  * @param  None
  * @retval None
  */
__INLINE void Configure_GPIO_I2C1(void)
{
  /* Enable the peripheral clock of GPIOB */
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
  /* (1) open drain for I2C signals */
  /* (2) AF1 for I2C signals */
  /* (3) Select AF mode (10) on PB6 and PB7 */
  GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7; /* (1) */
  GPIOB->AFR[0] = (GPIOB->AFR[0] & ~(GPIO_AFRL_AFRL6 | GPIO_AFRL_AFRL7)) \
                  | (1 << ( 6 * 4 )) | (1 << (7 * 4)); /* (2) */
  GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7)) \
                 | (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1); /* (3) */

}

/**
  * @brief  This function configures DMA for I2C1.
  * @param  None
  * @retval None
  */
__INLINE void Configure_DMA_I2C1(void)
{
  /* Enable the peripheral clock DMA1 */
  RCC->AHBENR |= RCC_AHBENR_DMA1EN;
 
   /* DMA1 Channel2 I2C1_RX config */
  /* (4)  Peripheral address */
  /* (5)  Memory address */
  /* (6)  Data size */
  /* (7)  Memory increment */
  /*      Peripheral to memory*/
  /*      8-bit transfer */
  /*      Transfer complete IT */
  DMA1_Channel3->CPAR = (uint32_t)&(I2C1->RXDR); /* (4) */
  DMA1_Channel3->CMAR = (uint32_t)datatoreceive; /* (5) */
  DMA1_Channel3->CNDTR = SIZE_OF_DATA; /* (6) */
  DMA1_Channel3->CCR |= DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN; /* (7) */
 
  /* Configure IT */
  /* (8) Set priority for DMA1_Channel2_3_IRQn */
  /* (9) Enable DMA1_Channel2_3_IRQn */
  NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0); /* (8) */
  NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); /* (9) */
}

/**
  * @brief  This function configures I2C1, slave.
  * @param  None
  * @retval None
  */
__INLINE void Configure_I2C1_Slave(void)
{
  /* Configure RCC for I2C1 */
  /* (1) Enable the peripheral clock I2C1 */
  /* (2) Use SysClk for I2C CLK */
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; /* (1) */
  RCC->CFGR3 |= RCC_CFGR3_I2C1SW; /* (2) */
 
  /* Configure I2C1, slave */
  /* (2) Timing register value is computed with the AN4235 xls file,
   fast Mode @400kHz with I2CCLK = 48MHz, rise time = 140ns, fall time = 40ns */
  /* (3) Periph enable, receive DMA enable */
  /* (4) 7-bit address = 0x5A */
  /* (5) Enable own address 1 */
  I2C1->TIMINGR = (uint32_t)0x00B00000; /* (2) */
  I2C1->CR1 = I2C_CR1_PE | I2C_CR1_RXDMAEN | I2C_CR1_ADDRIE; /* (3) */
  I2C1->OAR1 |= (uint32_t)(I2C1_OWN_ADDRESS << 1); /* (4) */
  I2C1->OAR1 |= I2C_OAR1_OA1EN; /* (5) */
 
  /* Configure IT */
  /* (7) Set priority for I2C1_IRQn */
  /* (8) Enable I2C1_IRQn */
  NVIC_SetPriority(I2C1_IRQn, 0); /* (7) */
  NVIC_EnableIRQ(I2C1_IRQn); /* (8) */
}

/**
  * @brief  This function :
             - Enables GPIO clock
             - Configures the I2C2 pins on GPIO PB10 PB11
  * @param  None
  * @retval None
  */
__INLINE void Configure_GPIO_I2C2(void)
{
  /* Enable the peripheral clock of GPIOB */
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    
  /* (1) Open drain for I2C signals */
  /* (2) AF1 for I2C signals */
  /* (3) Select AF mode (10) on PB10 and PB11 */
  GPIOB->OTYPER |= GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11; /* (1) */
  GPIOB->AFR[1] = (GPIOB->AFR[1] &~ (GPIO_AFRH_AFRH2 | GPIO_AFRH_AFRH3)) \
                  | (1 << (2 * 4)) | (1 << (3 * 4)); /* (2) */
  GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11)) \
                 | (GPIO_MODER_MODER10_1 | GPIO_MODER_MODER11_1); /* (3) */
 
}

/**
  * @brief  This function configures DMA for I2C2.
  * @param  None
  * @retval None
  */
__INLINE void Configure_DMA_I2C2(void)
{
  /* Enable the peripheral clock DMA1 */
  RCC->AHBENR |= RCC_AHBENR_DMA1EN;
 
  /* DMA1 Channel4 I2C2_TX config */
  /* (1)  Peripheral address */
  /* (2)  Memory address */
  /* (3)  Memory increment */
  /*      Memory to peripheral */
  /*      8-bit transfer */
  DMA1_Channel4->CPAR = (uint32_t)&(I2C2->TXDR); /* (1) */
  DMA1_Channel4->CMAR = (uint32_t)datatosend; /* (2) */
  DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR; /* (3) */
}

/**
  * @brief  This function configures I2C2, master.
  * @param  None
  * @retval None
  */
__INLINE void Configure_I2C2_Master(void)
{
  /* Enable the peripheral clock I2C2 */
  RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;

  /* Configure I2C2, master */
  /* (1) Timing register value is computed with the AN4235 xls file,
   fast Mode @400kHz with I2CCLK = 48MHz, rise time = 140ns, fall time = 40ns */
  /* (2) Periph enable */
  /* (3) Slave address = 0x5A, write transfer, 2 bytes to transmit, autoend */
  I2C2->TIMINGR = (uint32_t)0x00B01A4B; /* (1) */
  I2C2->CR1 = I2C_CR1_PE | I2C_CR1_TXDMAEN; /* (2) */
  I2C2->CR2 =  I2C_CR2_AUTOEND | (SIZE_OF_DATA << 16) | (I2C1_OWN_ADDRESS<<1); /* (3) */
}

/**
  * @brief  This function :
             - Enables GPIO clock
             - Configures the Push Button GPIO PA0
  * @param  None
  * @retval None
  */
__INLINE void Configure_GPIO_Button(void)
{
  /* Enable the peripheral clock of GPIOA */
  RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    
  /* Select mode */
  /* Select input mode (00) on PA0 */
  GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER0));
}

/**
  * @brief  This function configures EXTI.
  * @param  None
  * @retval None
  */
__INLINE void Configure_EXTI(void)
{
  /* Configure Syscfg, exti and nvic for pushbutton PA0 */
  /* (1) PA0 as source input */
  /* (2) unmask port 0 */
  /* (3) Rising edge */
  /* (4) Set priority */
  /* (5) Enable EXTI0_1_IRQn */
  SYSCFG->EXTICR[0] = (SYSCFG->EXTICR[0] & ~SYSCFG_EXTICR1_EXTI0) | SYSCFG_EXTICR1_EXTI0_PA; /* (1) */
  EXTI->IMR |= EXTI_IMR_MR0; /* (2) */
  EXTI->RTSR |= EXTI_RTSR_TR0; /* (3) */
  NVIC_SetPriority(EXTI0_1_IRQn, 0); /* (4) */
  NVIC_EnableIRQ(EXTI0_1_IRQn); /* (5) */
}

/******************************************************************************/
/*            Cortex-M0 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
}


/**
  * @brief  This function handles EXTI 0 1 interrupt request.
  * @param  None
  * @retval None
  */
void EXTI0_1_IRQHandler(void)
{
  EXTI->PR |= 1;    

  /* start I2C master transmission sequence */
  if((I2C2->ISR & I2C_ISR_TXE) == (I2C_ISR_TXE)) /* Check Tx empty */
  {
   // I2C2->TXDR = I2C_BYTE_TO_SEND; /* Byte to send */
    //I2C2->CR2 |= I2C_CR2_START; /* Go */
    /* start transmission with DMA */
    DMA1_Channel4->CCR &=~ DMA_CCR_EN;
    DMA1_Channel4->CNDTR = SIZE_OF_DATA;/* Data size */
    DMA1_Channel4->CCR |= DMA_CCR_EN;
    
    I2C2->CR2 |= I2C_CR2_START; /* Go */
  }
}

/**
  * @brief  This function handles I2C1 interrupt request.
  * @param  None
  * @retval None
  */
void I2C1_IRQHandler(void)
{
  uint32_t I2C_InterruptStatus = I2C1->ISR; /* Get interrupt status */
 
  if((I2C_InterruptStatus & I2C_ISR_ADDR) == I2C_ISR_ADDR)
  {
    I2C1->ICR |= I2C_ICR_ADDRCF; /* Address match event */
  }
}

/**
  * @brief  This function handles DMA1 channel 2 and 3 interrupt request.
  * @param  None
  * @retval None
  */
void DMA1_Channel2_3_IRQHandler(void)
{
  if((DMA1->ISR & DMA_ISR_TCIF3) == DMA_ISR_TCIF3)
  {
    DMA1->IFCR |= DMA_IFCR_CTCIF3;/* Clear TC flag */

    if(datatoreceive[0] == I2C_CMD_TOGGLE_ORANGE)
    {
      datatoreceive[0]=0;
      GPIOC->ODR ^= GPIO_ODR_8; /* Toggle orange LED */
    }
    
    if(datatoreceive[1] == I2C_CMD_TOGGLE_GREEN)
    {
      datatoreceive[1]=0;
      GPIOC->ODR ^= GPIO_ODR_9; /* toggle green LED */
    }
    
    DMA1_Channel3->CCR &=~ DMA_CCR_EN;
    DMA1_Channel3->CNDTR = SIZE_OF_DATA;/* Data size */
    DMA1_Channel3->CCR |= DMA_CCR_EN;
  }
  else
  {
    NVIC_DisableIRQ(DMA1_Channel2_3_IRQn);/* Disable DMA1_Channel2_3_IRQn */
  }
}

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

 

Share this post


Link to post
Share on other sites
21 час назад, Donker сказал:

...если попытаюсь вместо I2C_SendAddr использовать  I2C_SendByte, адрес отправляется, экран нормально реагирует, но  происходит бесконечное зависание на этапе ожидания бита подтверждения отправки байта:


while (0 == (I2C1->SR1 & I2C_SR1_TXE)){}

А Вы RM открывали хотя бы? Там же написано, какой флаг взведется после отправки адреса подчиненного, и это флаг - не TXE.

 

Цитата

void I2C_StartCondition(void) //СТАРТ
{
  I2C1->CR1 |= I2C_CR1_START;       
  while (!(I2C1->SR1 & I2C_SR1_SB)){}
  (void) I2C1->SR1; // зачем? Он итак прочитается в цикле выше
}

 

Цитата

void I2C_SendAddr(void)
{
  I2C1->DR = NAME;
  while (!(I2C1->SR1 & I2C_SR1_ADDR)){} // к моему первому вопросу
  (void) I2C1->SR1; // зачем? Он итак прочитается в цикле выше
  (void) I2C1->SR2;
}

 

2 часа назад, Donker сказал:

Если раскомментировать в функции OLED_SendScreen пересылку без DMA - тогда работает, если выполнить OLED_SendScreen в том виде как сейчас, в ЛА видно что происходит "условие старт", после чего линии лежат в нуле и ничего не передаётся.

Опять смотрю и только убеждаюсь, что RM Вы даже не открывали:wacko2:

Цитата

void OLED_SendScreen(void)
{
  DMA1_Channel4->CCR &=~ DMA_CCR4_EN;
  DMA1_Channel4->CNDTR = 513;/* Data size */
  DMA1_Channel4->CCR |= DMA_CCR4_EN;

  I2C1->CR1 |= I2C_CR1_START; /* Go */
  // дальше-то что? Думаете, тут DMA все дальше сам разрулит? Не-а.
}

 

Не забудьте, кстати, что, работая по DMA, разрешать прерывания по событиям TXE и RXNE уже нельзя, о чем можете также прочитать в RM.

 

P.S. Приведу Вам упрощенный псевдокод работы с I2C через DMA, правда на STM32F4xx (вроде у всех линеек эти модули отличаются не сильно):

void HW_I2CTransmitData(u8 *Buffer, u32 Size)
{
  HW_DMASetup(Buffer, Size);
  HW_I2CGenerateStart();
}

void ISR_I2C_HANDLER(void)
{
  if(HW_I2CCheckSR(SB))
  {
    HW_I2CSendAddress(HW_I2C_ADDRESS_ABONENT);
    return;
  }
  
  if(HW_I2CCheckSR(ADDR))
  {
    HW_DMAEnable();
    (void)HW_I2CCheckSR(TRA);
  }
  
  if(HW_I2CCheckSR(BTF))
  {
    HW_I2CGenerateStop();
    (void)HW_I2CReveiveData();
    
    OS_EventGive(OS_I2C_TXEND_EVENT);
  }
}

void ISR_DMA_HANDLER(void)
{
  if(HW_DMACheckErrors(&System.I2C.DMAErrors))
    OS_EventGive(OS_I2C_TXEND_EVENT);
}

 

Думаю, комментарии не нужны, но если будут вопросы - задавайте.

Share this post


Link to post
Share on other sites
59 минут назад, Arlleex сказал:

(вроде у всех линеек эти модули отличаются не сильно)

F0 и F4 отличаются радикально. Даже и не вспомню, какой из них более неудобный.

Share this post


Link to post
Share on other sites

Пока решил заняться STM32F030F4, для STM32F030F4 у меня работает только ногодрыг, не получается запустить аппаратный I2C, вот часть кода:

 

Спойлер

 


//--------------------------------------------------------------------------------------------
#define    OLED_addr_127 0b00111100        //7-битный адрес OLED         0b01111000 = 0x78
//--------------------------------------------------------------------------------------------
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; //включаем тактирование

GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10;   /* (1) Open drain for I2C signals */
GPIOA->AFR[1] = (GPIOA->AFR[1] &~ (GPIO_AFRH_AFRH1 | GPIO_AFRH_AFRH2)) | (0x01 << (1 * 4)) | (0x01 << (2 * 4));    /* (2) AF1 for I2C signals */
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10)) | (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1);   /* (3) Select AF mode (10) on PA9 and PA10 */
//--------------------------------------------------------------------------------------------
void I2C_init(void)
{
 
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;  /* Enable the peripheral clock I2C2 */
RCC->CFGR3 |= RCC_CFGR3_I2C1SW;    

/* Configure I2C2, master */
/* (1) Timing register value is computed with the AN4235 xls file,
 fast Mode @400kHz with I2CCLK = 48MHz, rise time = 140ns, fall time = 40ns */
/* (2) Periph enable */
/* (3) Slave address = 0x5A, write transfer, 1 byte to transmit, autoend */
I2C1->TIMINGR = (uint32_t)0x00B01A4B; /* (1) */
I2C1->CR1 = I2C_CR1_PE; /* (2) */
I2C1->CR2 =  I2C_CR2_AUTOEND | (1<<16) | (OLED_addr_127<<1); /* (3) */

}
//--------------------------------------------------------------------------------------------
void I2C_StartCondition(void) //СТАРТ
{
I2C1->CR2 |= I2C_CR2_START; /* Go */
}
//--------------------------------------------------------------------------------------------
void I2C_StopCondition(void) //СТОП
{
I2C1->CR2 |= I2C_CR2_STOP;
}
//--------------------------------------------------------------------------------------------
void I2C_SendByte(unsigned char data) //ПЕРЕДАЧА БАЙТА и АСК
{
if((I2C1->ISR & I2C_ISR_TXE) == (I2C_ISR_TXE)) /* Check Tx empty */
  {    
  I2C1->TXDR = data; /* Byte to send */
    I2C1->CR2 |= I2C_CR2_START; /* Go */    
    }
//while (0 == (I2C1->ISR & I2C_ISR_TXE)){}    
}
//--------------------------------------------------------------------------------------------

 

может кто нибудь заметит ошибку, пока что в ЛА невидно вообще никакой активности на линиях I2C
они сидят в высоком уровне даже в момент ресета.

Share this post


Link to post
Share on other sites

Нашёл ошибку - перепутал AF4 на котором сидит I2C в STM32F030F4  cо смещением номера вывода, правильно так:

GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10;                                                                    /* (1) Open drain for I2C signals */
GPIOA->AFR[1] = (GPIOA->AFR[1] &~ (GPIO_AFRH_AFRH1 | GPIO_AFRH_AFRH2)) | (4 << ((9 - 8) * 4)) | (4 << ((10 - 8) * 4));    /* (2) AF4 for I2C signals */
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10)) | (GPIO_MODER_MODER9_1 | GPIO_MODER_MODER10_1); /* (3) Select AF mode (0b10) on PA9 and PA10 */

Поток пошёл, сейчас разбираюсь с порядком следования байт.

Edited by Donker

Share this post


Link to post
Share on other sites

Возникли вопросы по регистру I2C1->CR2 в STM32F030F4:

Cудя по дащиту (RM стр. 518) там есть секция "NBYTES" которая имеет размерность 8 бит и куда
записывается количество байт для передачи (максимум 255), т.е. по максимуму это выглядит так:

I2C1->CR2 =  I2C_CR2_AUTOEND | (256<<16) | (OLED_addr_127<<1); /* (3) */

я правильно понимаю, что этота секция не даст отправить вподряд более 255 байт между "старт-адрес-..байты...-стоп"?

Этот же регистр не даст передать по DMA более 255 байт вподряд, хотя у DMA есть регистр DMA1_Channel2->CNDTR
куда можно записать уже 16 бит ?

Share this post


Link to post
Share on other sites

Ладно, допустим с выводом за раз, и без прерываний, массива более 255 байт на STM32F0 через DMA я пролетаю, но как
отправить хотя бы 255 байт?

 Вот так:

 

 

//--------------------------------------------------------------------------------------------
	uint8_t screen_buff[512];
#define    OLED_addr_127 0b00111100        //7-битный адрес OLED         0b01111000 = 0x78
 
//--------------------------------------------------------------------------------------------
	void I2C_init(void)
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* Enable the peripheral clock DMA1 */
	/* DMA1 Channel4 I2C1_TX config */ /*      Memory to peripheral */ /*      8-bit transfer */
DMA1_Channel2->CPAR = (uint32_t)&(I2C1->TXDR);    /* (1)  Peripheral address */
DMA1_Channel2->CMAR = (uint32_t)screen_buff;      /* (2)  Memory address */
DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_DIR; /* (3)  Memory increment */
    
//------------------------------------------------------------------------------------    
    
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;  /* Enable the peripheral clock I2C1 */
RCC->CFGR3 |= RCC_CFGR3_I2C1SW;    
	/* Configure I2C1, master */
/* (1) Timing register value is computed with the AN4235 xls file,
 fast Mode @400kHz with I2CCLK = 48MHz, rise time = 140ns, fall time = 40ns */
/* (2) Periph enable */
/* (3) Slave address = 0x5A, write transfer, 1 byte to transmit, autoend */
I2C1->TIMINGR = (uint32_t)0x00B01A4B;                         /* (1) */
I2C1->CR1 = I2C_CR1_PE;                                       /* (2) */
I2C1->CR2 = I2C_CR2_AUTOEND | (255<<16) | (OLED_addr_127<<1); /* (3) */
}
	//--------------------------------------------------------------------------------------------
	void OLED_SendScreen(void)
{    
if((I2C1->ISR & I2C_ISR_TXE) == (I2C_ISR_TXE)) /* Check Tx empty */
  {
  // I2C2->TXDR = I2C_BYTE_TO_SEND; /* Byte to send */
    //I2C2->CR2 |= I2C_CR2_START; /* Go */
    /* start transmission with DMA */
    DMA1_Channel2->CCR &=~ DMA_CCR_EN;
    DMA1_Channel2->CNDTR = 255; /* Data size */
    DMA1_Channel2->CCR |= DMA_CCR_EN;
    
    I2C1->CR2 |= I2C_CR2_START; /* Go */
  }        
}    
//--------------------------------------------------------------------------------------------

 

 

 

 

 

не отправляется, в ЛА видно что есть старт, и правильно с нормальной скоростью отправляется адрес устройства,
и всё, больше никаких байт не идёт и стопа тоже нет.
 Что не так?

Код практически целиком взят из снипетов (см. 1 пост),  только изменён I2C2 на  I2C1 и канал DMA c 4 на 2
 (в снипетах код для STM32F072RBT)

Edited by Donker

Share this post


Link to post
Share on other sites

Странный у Вас RM (или у меня?), раз на 518 странице у меня раздел RTC...

Приводя актуальный источник, делайте на него ссылку, чтобы не пришлось додумывать, какая версия документа у Вас используется.

RM на STM32F030, стр. 537:

Цитата

Hardware transfer management

...

The number of bytes to be transferred is programmed in the NBYTES[7:0] bit field in the I2C_CR2 register. If the number of bytes to be transferred (NBYTES) is greater than 255, or if a receiver wants to control the acknowledge value of a received data byte, the reload mode must be selected by setting the RELOAD bit in the I2C_CR2 register. In this mode, TCR flag is set when the number of bytes programmed in NBYTES has been transferred, and an interrupt is generated if TCIE is set. SCL is stretched as long as TCR flag is set. TCR is cleared by software when NBYTES is written to a non-zero value.

 

Скорее всего, делается это так. Допустим, нужно передать 352 байта. 352 это больше 255, поэтому настраиваем DMA на 352 байта, а NBYTES на 255 байт. Запускаем DMA. В прерывании по TCR (передано 255 байт), перезаписываем NBYTES на 352-255 = 97 байт. В следующем таком прерывании формируем Stop-условие (или в первом заходе в прерывание сбрасываем RELOAD, Stop-условие сформируется автоматически). Вроде так.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this