Проблемы с SPI на МК STM32F302C6T6 [РЕШЕНО]

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
A-Stim
Родился
Сообщения: 4
Зарегистрирован: Ср ноя 02, 2016 09:22:08
Откуда: Пенза

Проблемы с SPI на МК STM32F302C6T6 [РЕШЕНО]

Сообщение A-Stim »

Всем доброго времени суток, уважаемые форумчане!

Всё время старался разбираться сам, если возникают какие либо проблемы в познании такого ремесла, как программирование МК, но настало то время, когда без помощи более мудрых наставников никак не обойтись.

История началась с того, что программированием я занимаюсь меньше года, за это время успел освоить asm в любительской форме, а тут по работе нужда возникла перейти с PIC-ов на STM, заказали платы отладочные для экспериментов, вроде всё хорошо. Но возникла необходимость самим спаять плату с акселерометром LIS3DH на борту, а для управления взять камень STM32F302C6T6 (управление по шине SPI).

Плата готова, элементы напаяны, решаю начать с настройки тактирования от внешнего кварца и с типичного мигания светодиодом, дабы подтвердить работоспособность камня. Среда - keil uvision 5, библиотека - SPL.

Код: Выделить всё

int main(void)
{
	RCC_Configuration();

//---------------------------------------
//	Тактирование светодиодов
//---------------------------------------
	LEDS_ini();
//---------------------------------------
//							DELAY INIT
//---------------------------------------
	SysTick_Config(SystemCoreClock / 4000); // настройка появлений прерывания от SysTick на время 1ms
	delay_ms(1000);

	while(1)	
	{

		RED_ON;
		delay_ms(500);
		RED_OFF;
		delay_ms(500);
	}
}
Инициализация порта под светодиод и настройка тактирования от внешнего кварца:

Код: Выделить всё

#include "mcu_ini.h"

//=====================
//
//=====================
ErrorStatus HSEStartUpStatus;
RCC_ClocksTypeDef RCC_Clocks;

void RCC_Configuration(void)
{
		
		RCC_DeInit();                                               // Выполняем сброс reset 
    RCC_HSEConfig(RCC_HSE_ON);                                  // Включаем тактирование HSE (от кварца) 
    HSEStartUpStatus = RCC_WaitForHSEStartUp();                 // Ждем пока частота кварца стабилизируется

    if (HSEStartUpStatus == SUCCESS)                            // Если все отлично, то переходим на кварц
    {
        RCC_HCLKConfig(RCC_SYSCLK_Div2);                
        RCC_PCLK2Config(RCC_HCLK_Div1);                         // Запитываем APB2 от тактовой частоты PLL в 16 МГц
        RCC_PCLK1Config(RCC_HCLK_Div1);                         // Запитываем APB1 от тактовой частоты PLL в 16 МГц
        RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1);
				RCC_USARTCLKConfig(RCC_USART1CLK_PCLK);
        RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_4);     // Устанавливаем множитель частоты 4
				RCC_PREDIV1Config(RCC_PREDIV1_Div1);
        
        RCC_PLLCmd(ENABLE);                                     // Включаем PLL
        
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}  // Ждем пока PLL устаканится
        
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);              // Выбираем PLL как источник тактового сигнала

        
        while (RCC_GetSYSCLKSource() != 0x08) {}                // Ждем пока PLL установится как источник тактирования
				RCC_HSICmd(DISABLE);
    }
		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF,ENABLE);
}
//=====================
//
//=====================
void LEDS_ini(void)
{
	GPIO_InitTypeDef GPIO_init_LED;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);
	
	GPIO_init_LED.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_init_LED.GPIO_OType = GPIO_OType_PP;
	GPIO_init_LED.GPIO_Pin = GPIO_Pin_1;
	GPIO_init_LED.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_init_LED.GPIO_Speed = GPIO_Speed_2MHz;
	
	GPIO_Init(GPIOA,&GPIO_init_LED);
}
Всё работает. Думаю, уже хорошо, можно настроить SPI и опросить регистр WHO_I_AM акселерометра, пишу:
- инициализация SPI2

Код: Выделить всё

void SPI_ini(void)
{
	GPIO_InitTypeDef 			GPIO_init_SPI;
	SPI_InitTypeDef				SPI_init_user;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	
	//=====================		
	GPIO_init_SPI.GPIO_Mode = GPIO_Mode_AF;
	GPIO_init_SPI.GPIO_OType = GPIO_OType_PP;
	GPIO_init_SPI.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_init_SPI.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_init_SPI.GPIO_PuPd = GPIO_PuPd_UP;
	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_5); //настройка альтернативной функции ножек порта
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5);

	//===================== Chip Select
	GPIO_init_SPI.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_init_SPI.GPIO_OType = GPIO_OType_PP;
	GPIO_init_SPI.GPIO_Pin = GPIO_Pin_12;
	GPIO_init_SPI.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_init_SPI.GPIO_PuPd = GPIO_PuPd_UP;

	GPIO_Init(GPIOB,&GPIO_init_SPI);
  //=====================
	CS_OFF;
	//=====================
	SPI_I2S_DeInit(SPI2);
	
	SPI_init_user.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_init_user.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_init_user.SPI_CPOL = SPI_CPOL_Low;
	SPI_init_user.SPI_CRCPolynomial = 7;
	SPI_init_user.SPI_DataSize = SPI_DataSize_8b;
	SPI_init_user.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_init_user.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_init_user.SPI_Mode = SPI_Mode_Master;
	SPI_init_user.SPI_NSS = SPI_NSS_Soft;
	
	
	SPI_Init(SPI2,&SPI_init_user);
	//=====================
	
	SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
 
  SPI_DataSizeConfig(SPI2, ENABLE);
	
	SPI_Cmd(SPI2,ENABLE);

	SPI_NSSInternalSoftwareConfig(SPI2, SPI_NSSInternalSoft_Set);
Для пояснения: CS - это нога акселерометра, отвечающая за выбор типа обмена (SPI или I2C). Сам обмен осуществляется только после подтягивания данной ноги к земле, после окончания передачи нога снова подтягивается к питанию.

Код: Выделить всё

#define	CS_ON									GPIO_ResetBits(GPIOB,GPIO_Pin_12);
#define	CS_OFF								GPIO_SetBits	(GPIOB,GPIO_Pin_12);
Функция для чтения данных:

Код: Выделить всё

//Передача данных
uint8_t Send_data (uint8_t byteToSend)
	{
		while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) {}
			
		SPI_SendData8(SPI2,byteToSend);
			
		while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){}	
			
		return (uint8_t)SPI_ReceiveData8(SPI2);		
	}
//Функция чтения данных (addr - адрес)
	
uint8_t	Read_data (uint8_t addr)
	{	
		uint8_t temp_byte;
		
		CS_ON;
	
		Send_data (addr);
	
		temp_byte = Send_data (DUMMY_BYTE);
	
		CS_OFF;

		return temp_byte;		
	}
Программа для опроса регистра:

Код: Выделить всё

void	Accel_Ini (void)
	{
	uint8_t Accel_ID;
//-------------------------------------------------------------
//	Проверка акселерометра опросом его ID. 
//	При верном полученном значении происходит инициализация
///-------------------------------------------------------------
		Accel_ID = Read_data(LIS3DSH_WHO_I_AM_ADDR);
		
			if (Accel_ID == 0x33)
			{
				RED_ON;
				delay_ms(500);
				RED_OFF;
				Accel_setup ();			
			}
			else
			{
				Error();
			}
	}
И сам main:

Код: Выделить всё

int main(void)
{
	RCC_Configuration();

//---------------------------------------
//	Тактирование светодиодов
//---------------------------------------
	LEDS_ini();
//---------------------------------------
//							DELAY INIT
//---------------------------------------
	SysTick_Config(SystemCoreClock / 4000); // настройка появлений прерывания от SysTick на время 1ms
	delay_ms(1000);
//---------------------------------------
//---------------------------------------
//							SPI INIT
//---------------------------------------	
	SPI_ini();
//---------------------------------------

//=====================
	while(1)	
	{
		Accel_Ini();
	}
}
Прошиваю, запускаю: не работает. висит на опросе

Код: Выделить всё

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET){}	
Лезу с осциллографом на ногу SCK, по которой осуществляется тактирование SPI, там пусто. Пробую тот же самый код, но немного переделанный под отладочную плату STM32F3Discovery и опрашиваю гироскоп L3GD20, который там стоит. Всё опрашивается, и значение с регистра приходит то, которое нужно. Иду дальше, пробую создать проект в STM32Cube, меняю функции SPL-я на HAL-овские, зашиваю свой камень, SPI работает. Опрос происходит, данные приходят те, которые нужны.

В связи с этим вопрос к знающим котам: где может быть ошибка в листинге, который написан с помощью SPL? Возможно что то не включил ещё дополнительное?
Был бы очень благодарен за помощь!
PS: знаю, что некоторые люди скажут, мол "SPL,HAL - всё не то, CMSIS - вот решение проблем", я с ними соглашусь, но на углубленное изучение регистров и работы с ними времени надо достаточно, а у меня его на данный момент не так много, так что жду помощи :)
PS2: если вдруг нужен проект целиком, сообщите, я не жадный :)

UPDT: Я понял секрет решения проблем. Создаешь тему и ждешь, пока ответ найдется сам :)

Если вдруг кому-то будет интересен данный топик, то проблема в итоге решена:
Дело оказалось в основном в аппаратной части, по неопытности и малограмотности по семейству STM, я для удобства проектирование ПП вывод CS подключил на аппаратный NSS SPI2, думая, что раз я данный вывод не использую, то можно смело его задействовать в качестве стандартного порта ввода/вывода. Однако ТАК сие изделие у меня не заработало. Пришлось вывод перенести на другую свободную ногу. Однако про настройку NSS забывать не стоит:

Код: Выделить всё

SPI_I2S_DeInit(SPI2);
	
	SPI_init_user.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_init_user.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_init_user.SPI_CPOL = SPI_CPOL_High;
	SPI_init_user.SPI_CRCPolynomial = 7;
	SPI_init_user.SPI_DataSize = SPI_DataSize_8b;
	SPI_init_user.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_init_user.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_init_user.SPI_Mode = SPI_Mode_Master;
	SPI_init_user.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;
	
	SPI_Init(SPI2,&SPI_init_user);
	//=====================
	
	SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
 
        SPI_DataSizeConfig(SPI2, ENABLE);
	
	SPI_Cmd(SPI2,ENABLE);
Реклама
Аватара пользователя
scorpi_0n
Вымогатель припоя
Сообщения: 616
Зарегистрирован: Вс ноя 01, 2015 13:13:49

Re: Проблемы с SPI на МК STM32F302C6T6 [РЕШЕНО]

Сообщение scorpi_0n »

A-Stim писал(а): для удобства проектирование ПП вывод CS подключил на аппаратный NSS SPI2, думая, что раз я данный вывод не использую, то можно смело его задействовать в качестве стандартного порта ввода/вывода. Однако ТАК сие изделие у меня не заработало. Пришлось вывод перенести на другую свободную ногу.
Не разобрались до конца с настройками SPI. Можно смело его задействовать в качестве стандартного порта ввода/вывода.
Реклама
A-Stim
Родился
Сообщения: 4
Зарегистрирован: Ср ноя 02, 2016 09:22:08
Откуда: Пенза

Re: Проблемы с SPI на МК STM32F302C6T6 [РЕШЕНО]

Сообщение A-Stim »

scorpi_0n писал(а):
A-Stim писал(а): для удобства проектирование ПП вывод CS подключил на аппаратный NSS SPI2, думая, что раз я данный вывод не использую, то можно смело его задействовать в качестве стандартного порта ввода/вывода. Однако ТАК сие изделие у меня не заработало. Пришлось вывод перенести на другую свободную ногу.
Не разобрались до конца с настройками SPI. Можно смело его задействовать в качестве стандартного порта ввода/вывода.
Тогда вопрос, как можно было это сделать?
Я до этого использовал:

Код: Выделить всё

   SPI_NSSInternalSoftwareConfig(SPI2, SPI_NSSInternalSoft_Set);
Имеет ли значение то, что я использовал эту функцию до

Код: Выделить всё

 SPI_Cmd(SPI2,ENABLE);
или нет?
Ответить

Вернуться в «ARM»