Всем доброго времени суток, уважаемые форумчане! Всё время старался разбираться сам, если возникают какие либо проблемы в познании такого ремесла, как программирование МК, но настало то время, когда без помощи более мудрых наставников никак не обойтись.
История началась с того, что программированием я занимаюсь меньше года, за это время успел освоить 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);