|
STM32 <-spi-> внешний ADC |
|
|
|
May 27 2013, 15:51
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Господа, помогите разобраться. У меня есть STM32F3-Discovery, я хочу подключить к нему через SPI ацп AD7738 ( pdf). Решил использовать SPI2, в даташите STM32F3 прочитал, что в SPI2 задействованы ноги PB13, PB14, PB15. Настроил GPIO для этих пинов, настроил GPIO для RDY пина, на который пишется нолик, когда АЦП завершает преобразование (выбрал PD10), создал SPI2 с некими параметрами. Для конфигурации я сделал ряд функций, которыми можно изменять параметры регистров, чтобы каждый раз не писать 0b00... (см. дефайны и ADC_ функции в коде) Суть в том, что я использовал сначала пример с прерываниями и он работал, но через ж... не совсем как я хочу. Там я отправляю байтики функцией SPI_I2S_SendData16 и они приходят в функцию прерывания. Проблема в том, что я не могу понять, что я читаю, но по данным догадался, что приходит стандартное значение регистра данных (8000h), на датчик не реагирует АЦП. Поэтому я погуглил еще и просто сделал функции для чтения-записи 8бит данных вместо прерываний. Далее пишу 0x42 (перед mode-регистром, до начала преобразования), чтобы достать версию чипа, должно прийти "33" (dec), но приходит 0. С прерываниями работало и приходило 33 (и это подходило под описание регистра ревизии). Также не приходит стандартное значение регистра данных, вместо этого приходит тупо 0. В чем ошибка моего варианта? Я подозреваю, что само соединение как-то не правильно настроено. Код прилагаю Код #include "main.h" #include <stdbool.h> #include <math.h> #include "usb_CDC.h" #include "stm32f30x_gpio.h" #include "stm32f30x_spi.h"
/*******************************************************************************/ __IO uint32_t TimingDelay = 0; __IO uint32_t UserButtonPressed = 0; __IO uint8_t DataReady = 0; __IO uint8_t PrevXferComplete = 1; __IO uint32_t USBConnectTimeOut = 100; volatile int rx, rx_data;
union Pack16bitk{ char b[sizeof(short)]; short sval; } Package16;
/* * AIN0: 000 * AIN1: 001 * AIN2: 010 * AIN3: 011 * AIN4: 100 * AIN5: 101 * AIN6: 110 * AIN7: 111 */
#define SELECH 0b00000111
#define IOPR_P0_OUTPUT_0 0b00000000 #define IOPR_P0_OUTPUT_1 0b10000000 #define IOPR_P0_DIR_AIN 0b01000000 #define IOPR_P0_DIR_AOUT 0b00000000 #define IOPR_P1_DIR_DIN 0b00100000 #define IOPR_P1_DIR_DOUT 0b00000000 #define IOPR_RDYFN_ANY 0b00000000 #define IOPR_RDYFN_ALL 0b00001000 #define IOPR_SYNC_ON 0b00000001 #define IOPR_SYNC_OFF 0b00000000
#define CHSETR_BUF_ENABLE 0b00000000 #define CHSETR_BUF_DISABLE 0b10000000 #define CHSETR_AINCONF_0 0b00000000 #define CHSETR_AINCONF_1 0b01100000 #define CHSETR_STATOPT_P1 0b00010000 #define CHSETR_STATOPT_RDY 0b00000000 #define CHSETR_ENABLE_ENABLE 0b00001000 #define CHSETR_ENABLE_DISABLE 0b00000000 #define CHSETR_RANGE_0 0b00000100 // +-2.5V #define CHSETR_RANGE_1 0b00000101 // 0V to +2.5V #define CHSETR_RANGE_2 0b00000000 // +-1.25V #define CHSETR_RANGE_3 0b00000001 // 0V to +1.25V #define CHSETR_RANGE_4 0b00000010 // +-0.625V #define CHSETR_RANGE_5 0b00000011 // 0V to +0.625V
#define CHCONVTIMER_CHOP_ON 0b10000000 #define CHCONVTIMER_CHOP_OFF 0b00000000 #define CHCONVTIMER_FILTER_0 0b01111111 //FW=127, Conv.Time = 2686 us, Out data rate = 372Hz [With chopping enabled] #define CHCONVTIMER_FILTER_1 0b00101110 //FW=46, Conv.Time = 999 us, Out data rate = 1001 Hz #define CHCONVTIMER_FILTER_2 0b00010001 //FW=17, Conv.Time = 395 us, Out data rate = 2534 Hz #define CHCONVTIMER_FILTER_3 0b00001000 //FW=8, Conv.Time = 207 us, Out data rate = 4826 Hz #define CHCONVTIMER_FILTER_4 0b00000100 //FW=4, Conv.Time = 124 us, Out data rate = 8074 Hz #define CHCONVTIMER_FILTER_5 0b00000010 //FW=2, Conv.Time = 82 us, Out data rate = 12166 Hz
#define MODER_MODE_CONTINUOUS 0b00100000 #define MODER_MODE_SINGLE 0b01000000 #define MODER_CLKDIS_ON 0b00010000 #define MODER_CLKDIS_OFF 0b00000000 #define MODER_DUMP_ON 0b00001000 #define MODER_DUMP_OFF 0b00000000 #define MODER_CONTRD_ON 0b00000100 #define MODER_CONTRD_OFF 0b00000000 #define MODER_BITS_24 0b00000010 #define MODER_BITS_16 0b00000000 #define MODER_CLAMP_ON 0b00000001 #define MODER_CLAMP_OFF 0b00000000
/*******************************************************************************/ void USB_Config(void){ Set_System(); Set_USBClock(); USB_Interrupts_Config(); USB_Init();
while ((bDeviceState != CONFIGURED)&&(USBConnectTimeOut != 0)) {} }
/*******************************************************************************/ void SPI_Config(void){ RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_5); GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5); GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5);
GPIO_InitTypeDef port;
port.GPIO_Mode = GPIO_Mode_AF; port.GPIO_OType = GPIO_OType_PP; port.GPIO_PuPd = GPIO_PuPd_DOWN; port.GPIO_Speed = GPIO_Speed_50MHz;
//SCK -> PB13 | MISO -> PB14 | MOSI -> PB15 port.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init(GPIOB, &port);
SPI_InitTypeDef spi; spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi.SPI_Mode = SPI_Mode_Master; spi.SPI_DataSize = SPI_DataSize_8b; spi.SPI_CPOL = SPI_CPOL_High; spi.SPI_CPHA = SPI_CPHA_2Edge; spi.SPI_NSS = SPI_NSS_Soft; spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; spi.SPI_FirstBit = SPI_FirstBit_MSB; spi.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &spi); SPI_Cmd(SPI2, ENABLE); //Включение SPI }
uint8_t SPI_SendRead8(uint8_t ToSend8){ while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET); SPI2->DR = ToSend8; while ((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET); return (SPI2->DR); }
void SPI_Send8(uint8_t ToSend8){ while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET); SPI2->DR = ToSend8; while ((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET); uint8_t dummy = (SPI2->DR); }
void ADC_Reset(){ SPI_Send8(0x00); SPI_Send8(0xff); SPI_Send8(0xff); SPI_Send8(0xff); SPI_Send8(0xff); }
void ADC_IOpr(uint8_t P0, uint8_t P1, uint8_t P0_DIR, uint8_t P1_DIR, uint8_t RDYFN, uint8_t SYNC){ SPI_Send8(0b00000001); //IO Port Register SPI_Send8(P0+P1+P0_DIR+P1_DIR+RDYFN+SYNC); }
void ADC_ChSetr(uint8_t BUFOFF, uint8_t AINCONF, uint8_t STATOPT, uint8_t ENABLE, uint8_t RANGE){ SPI_Send8(0b00101000+SELECH); //Channel setup register SPI_Send8(BUFOFF+AINCONF+STATOPT+ENABLE+RANGE); }
void ADC_ChConvTimr(uint8_t CHOP, uint8_t FILTERWORD){ SPI_Send8(0b00110000+SELECH); //Channel conversion time register SPI_Send8(CHOP+FILTERWORD); }
void ADC_Moder(uint8_t MODER_MODE, uint8_t CLKDIS, uint8_t DUMP, uint8_t CONTRD, uint8_t MODER_BITS, uint8_t CLAMP){ SPI_Send8(0b00111000+SELECH); //Mode register SPI_Send8(MODER_MODE+CLKDIS+DUMP+CONTRD+MODER_BITS+CLAMP); }
/*******************************************************************************/ int main(void){ SystemInit(); RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); GPIO_InitTypeDef portd; portd.GPIO_Mode = GPIO_Mode_IN; portd.GPIO_PuPd = GPIO_PuPd_DOWN; portd.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOD, &portd);
USB_Config(); SPI_Config();
ADC_Reset(); ADC_IOpr(IOPR_P0_OUTPUT_1, IOPR_P0_OUTPUT_1, IOPR_P0_DIR_AIN, IOPR_P1_DIR_DOUT, IOPR_RDYFN_ANY, IOPR_SYNC_OFF); ADC_ChSetr(CHSETR_BUF_ENABLE, CHSETR_AINCONF_1, CHSETR_STATOPT_P1, CHSETR_ENABLE_ENABLE, CHSETR_RANGE_1); ADC_ChConvTimr(CHCONVTIMER_CHOP_ON, CHCONVTIMER_FILTER_2);
uint8_t ADC_Revision = SPI_SendRead8(0x42); //Должно быть 0x21
ADC_Moder(MODER_MODE_CONTINUOUS, MODER_CLKDIS_ON, MODER_DUMP_OFF, MODER_CONTRD_OFF, MODER_BITS_16, MODER_CLAMP_OFF);
while (1){ if(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_10) == 0){ //1=high 0=low SPI_Send8(0b1001000+SELECH);//1001000-1001111 Package16.b[0] = SPI_SendRead8(0x00); Package16.b[1] = SPI_SendRead8(0x00); USB_Send_Short(Package16.sval); } Delay(50); } }
/*******************************************************************************/ void Delay(__IO uint32_t nTime){ TimingDelay = nTime; while(TimingDelay != 0); }
/*******************************************************************************/ void TimingDelay_Decrement(void){ if (TimingDelay != 0x00) { TimingDelay--; } }
Сообщение отредактировал undef1ned - May 27 2013, 15:53
|
|
|
|
|
May 27 2013, 21:38
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Цитата(artkam @ May 28 2013, 00:49)  А где у Вас в коде управление ногой CHIP SELECT (CS) описано??? Что-то я найти не могу... Цитата With this input hardwired low, the AD7738 can operate in its 3-wire interface mode using SCLK, DIN, and DOUT. CS can be used to select the device in systems with more than one device on the serial bus. У меня как раз вот так сделано ну и только одно устройство на шине.
Сообщение отредактировал undef1ned - May 27 2013, 21:38
|
|
|
|
|
May 27 2013, 22:41
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Да, у меня CS (4 пин) подключен к GND на stm32f3-discovery, я проверил Еще приведу свой код с прерываниями, где поочередно приходят значения 0x8000, 0x80, 0x00, а кстати если отключить питание датчиков то приходит 0xE080, 0xE0, 0x8000 Собственно, та же программа, но есть ф-я Код void SPI2_IRQHandler(){ if (SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_RXNE) == SET){ uint16_t spi_data16 = 0x00; spi_data16 = SPI_I2S_ReceiveData16(SPI2); USB_Send_Short(spi_data16); } } В ней я ставлю бряк на USB_Send_Short() и смотрю spi_data16. Конф-я SPI, вроде такая же, ну помимо 16 бит размера, но с 8 бит то же самое: Код /*******************************************************************************/ void SPI_Config(void){ RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_5); GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5); GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5);
port.GPIO_Mode = GPIO_Mode_AF; port.GPIO_OType = GPIO_OType_PP; port.GPIO_PuPd = GPIO_PuPd_DOWN; port.GPIO_Speed = GPIO_Speed_50MHz;
//SCK -> PB13, MOSI -> PB15 port.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOB, &port);
port.GPIO_Pin = GPIO_Pin_13; GPIO_Init(GPIOB, &port);
//MISO -> PB14 port.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &port);
//SPI_StructInit(&spi); spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi.SPI_Mode = SPI_Mode_Master; spi.SPI_DataSize = SPI_DataSize_16b;//16 spi.SPI_CPOL = SPI_CPOL_High; spi.SPI_CPHA = SPI_CPHA_2Edge; spi.SPI_NSS = SPI_NSS_Soft; spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; spi.SPI_FirstBit = SPI_FirstBit_MSB; spi.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &spi); } Вместо ADC_ функций тупо шлю заранее записанные байты: Код void ADC_Config(void){ //00h + FFh + FFh + FFh + FFh (RESET) SPI_SendData8(SPI2, 0x00); SPI_SendData8(SPI2, 0xff); SPI_SendData8(SPI2, 0xff); SPI_SendData8(SPI2, 0xff); SPI_SendData8(SPI2, 0xff);
//Configure SPI_SendData8(SPI2, 0b00000001); //IO Port Registe SPI_SendData8(SPI2, 0b11110000); //
SPI_SendData8(SPI2, 0b00101000+SELECH); //Channel setup register SPI_SendData8(SPI2, 0b00011100); //+-2.5V, Single-ended ENABLE
SPI_SendData8(SPI2, 0b00110000+SELECH); //ch. conv. time, 110000-110111 SPI_SendData8(SPI2, 0b10010001); // }
void ADC_SetMODER(void){ SPI_SendData8(SPI2, 0b00111000+SELECH); //Mode register, 00111111-00111000 SPI_SendData8(SPI2, 0x00100000); } Ну и здесь вот с настрйокой прерываний: Код int main(void){ __enable_irq(); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); GPIO_InitTypeDef portd; portd.GPIO_Mode = GPIO_Mode_IN; portd.GPIO_PuPd = GPIO_PuPd_DOWN; portd.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOD, &portd);
SystemInit(); RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 100); USB_Config();
SPI_Config(); NVIC_EnableIRQ(SPI2_IRQn); SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE); SPI_Cmd(SPI2, ENABLE); SPI_NSSInternalSoftwareConfig(SPI2, SPI_NSSInternalSoft_Set);
ADC_Config(); ADC_SetMODER();
while (1){ //Poll RDY pin if(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_10) == 0){ //1=high 0=low SPI_SendData8(SPI2, 0b1001000+SELECH);//0x4F } Delay(50); } } Такие дела. Конечно неправильно отправлять просто 0x4F, там в дш после него еще 2 раза по 0х00 идет. Если поставить SPI_SendData8(0x00) 2 раза, то в ответ на это просто приходят нули (по крайней мере в этом все отличие).
Сообщение отредактировал undef1ned - May 27 2013, 22:42
|
|
|
|
|
May 28 2013, 13:13
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Ну собственно, от AD7738 к мк идут только SCLK, RDY, DIN, DOUT, RESET (dvdd), CS (dgnd), вроде ничего не забыл MUXOUT и ADCIN соединены (пины 13 и 16, 14 и 15) SCLK -> SCK (PB13) DIN ->MOSI (PB15) DOUT ->MISO (PB14) RDY -> PD10 MCLKIN, MCLKOUT к резонатору CS -> DGND RESET ->DVdd +3v DVdd (пин 27) -> DVdd AIN7 соединен с датчиком как показано на схеме в дш
Не надо ведь управлять сигналом SCLK вручную?
|
|
|
|
|
May 29 2013, 08:12
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Да, я так попробую сделать сейчас. спасибо за подсказку
|
|
|
|
|
May 29 2013, 14:05
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Вобщем подключил SS и дергаю его когда передаю-получаю данные. Для проверки посылаю 0x42, чтобы он мне вернул версию чипа. Судя по даташиту, должно прислать число, которое кончается на 0001, я его как-то раз совершенно случайно получил, это было 0х21, но вместо нее приходит 0x40. Логический анализатор мне показывает вот такую картину:  как видно, SS работает исправно, sclk тоже работает, MOSI отправляет данные и MISO принимает Я отправляю 0x42 в цикле с задержкой. Этот ответ чередуется с точно таким же только без 0x00 в MOSI и без 0x21 в MISO. Почему у меня 16 бит отправляется, я же выставил в настройках 8 битный режим? Почему на 0x42 приходит 0x40, а правильный ответ 0x21 только после него? Посылаю так: Код while(1){ uint8_t ADC_Revision = SPI_SendRead8(0x42); Delay(1); } Код uint8_t SPI_SendRead8(uint8_t ToSend8){ GPIO_WriteBit(GPIOD, GPIO_Pin_8, Bit_RESET); //<--SS=0 while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET); SPI2->DR = ToSend8; while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET); GPIO_WriteBit(GPIOD, GPIO_Pin_8, Bit_SET); //<--SS=1 return (SPI2->DR); }
|
|
|
|
|
May 29 2013, 16:47
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(undef1ned @ May 29 2013, 20:05)  Посылаю так: Код while(1){ uint8_t ADC_Revision = SPI_SendRead8(0x42); Delay(1); } А надо так: Код while(1){ uint8_t ADC_Revision; SPI_SendRead8(0x42); ADC_Revision = SPI_SendRead8(0); Delay(1); } Чтобы прочитать ответ от ведомого SPI-устройства, надо что-нибудь отправить в него.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
May 29 2013, 16:56
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Я думал, что то что я записываю 0х42 это и есть нужная запись. Попробую теперь с двойной записью.
|
|
|
|
|
May 29 2013, 18:11
|
Группа: Участник
Сообщений: 10
Регистрация: 27-05-13
Пользователь №: 76 996

|
Цитата(AHTOXA @ May 29 2013, 21:14)  Как, по-вашему, АЦП по первому биту команды 0x42 догадается, что это 0x42, чтобы сразу начать отправлять ответ?  offtopОднако, вот что происходит, когда я посылаю сначала 0х42, а потом 0х00: http://imgur.com/BAytYmIТакое впечатление, что он по 16 бит кидает сразу... хотя у меня настроено на 8 бит: Код spi.SPI_DataSize = SPI_DataSize_8b; edit: Код return (uint8_t)SPI2->DR >> 8; Вот так вытаскивает 0x21, оказывается 0x4021 приходит сразу в регистр когда я шлю 0x42, так должно быть?
Сообщение отредактировал undef1ned - May 29 2013, 18:11
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|