External 8-bit DAC Control with STM32F7 SPI - Page 1
Forum section: electronics / general

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 11, 2018, 06:16:36 pm
content: Hello,I try to control an external DAC with SPI communication in STM32F7. I see my Data Signals as inputs of DAC but the outputs of the DAC are just linear signal. I expect to get pulse signals as output. Has anyone any idea about it?My code:/* USER CODE BEGIN PV *//* Private variables ---------------------------------------------------------*/uint8_t dataTx={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};#define SpiEn HAL_GPIO_WritePin(GPIOG,GPIO_PIN_7,GPIO_PIN_RESET);#define SpiDis HAL_GPIO_WritePin(GPIOG,GPIO_PIN_7,GPIO_PIN_SET);/* USER CODE END PV */...while (1){SpiEn;HAL_SPI_Transmit(&hspi6, (uint8_t *)dataTx, 16, 100);SpiDis;while(HAL_SPI_GetState(&hspi6)!=HAL_SPI_STATE_READY);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */....

author: rstofer
author_rank: Super Contributor
timestamp: September 11, 2018, 06:33:23 pm
content: When you transmit a word, do you have to wait for the buffer to be emptied before disabling the SPI link?This is the very reason I bought the Rigol DS1054Z scope with SPI decoding. I wanted to see how the CS' signal framed the transaction.The problem with using HAL code (that somebody else wrote) is that you really don't know if the Transmit function is blocking until completion (probably not) or returning after just putting the value in the buffer or shift register (more likely). And it matters...Maybe the documentation talks about it.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 11, 2018, 11:20:35 pm
content: Yes, AFAIR the HAL_SPI_Transmit() function is a blocking transfer. There are variants of it for interrupt-based or DMA-based transfers. The HAL_SPI_GetState() test should not be necessary, I believe it's already called in the HAL_SPI_Transmit() function itself.Since the author "sees" the "data signals", we can assume they have a scope and have checked the output was at least as they intended? Surely SPI decoding is handy - hand decoding SPI on a scope is doable but pretty annoying.Since we have no idea about the DAC itself and whether it needs some kind of configuration, and whether what's written to it is correct, we can't comment further on that.One general remark though is that there is no pause whatsoever between 'SpiEn', the transfer and 'SpiDis'. We don't know what clock the STM32F7 runs at here, but it's a pretty fast device if it runs at full speed. The DAC may (and probably does) need a minimum time interval between the falling/rising edge of what is probably its CS pin and the data transfer itself. The SPI clock may also be too fast for the DAC specs. Some things to check.

author: newbrain
author_rank: Super Contributor
timestamp: September 12, 2018, 07:07:12 am
content: Quote from: A.Ersoz on September 11, 2018, 06:16:36 pmDisclaimer: English is my 3rd language.That said, could you please clarify what is meant by "linear" signal?Are you observing a constant value at the DAC output?I imagine you are expecting a sawtooth, is that right?Some minor considerations:The HAL_SPI_Transmit() function makes sure the SPI transaction is completed before returning (the last bit has been shifted out of MOSI / into MISO), for a successful execution the exit state is HAL_SPI_STATE_READY.That makes this line is redundant:Code:while(HAL_SPI_GetState(&hspi6)!=HAL_SPI_STATE_READY);The (uint8_t *) cast in the transmit call is also redundant, the array name will decay to a pointer in this context.But the major point I see is that the CS of the DAC will stay low during the whole array transmission.Many DACs latch the value to be converted on the rising edge of CS (e.g.MCP4802, see chap. 6.1), so this might be what's wrong.Which DAC are you using?Then, a couple of pet peeves of mine:SpiEnandSpiDisare defined as object-like macros. Since they perform an action, it would be clearer to define them as function-like macros, i.e.SpiEn()andSpiDis().Plese use the [ code] [ /code] tags (# button) when including code, it makes the post much more readable

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 13, 2018, 09:13:35 pm
content: Yes, I see a constant signal as an output. I can control the output voltage but this is still constant. DAC is DAC082S085 and MCU is STM32F767ZI.

author: HB9EVI
author_rank: Frequent Contributor
timestamp: September 13, 2018, 09:22:35 pm
content: If you expect changes in the output voltage, you have to continously send data to the DAC, just from itself this DAC is not doing that; but keep in mind that those SPI DACs have a rather low slew rate; this one has 1V/uS; there is not much headroom for pulses

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 13, 2018, 09:38:39 pm
content: Then I would add pauses as I suggested, and also include newbrain's suggestion to frame EACH new DAC word with CS. As he said, usually most SPI DACs latch the input data to the DAC register upon the rising edge of CS. They rarely support "burst mode".

author: newbrain
author_rank: Super Contributor
timestamp: September 14, 2018, 06:38:43 am
content: Quote from: A.Ersoz on September 13, 2018, 09:13:35 pmFrom thedatasheet(emphasis mine):QuoteSo you simply see the first value that's written after bringing SYNC low, as I suspected.pusles: I like this Texas-Rigol joint initiative!EtA: and of course, as SiliconWizard notes, make sure to take the settling time into account: tsis 4.5us worst case. The SPI seems to be fine for up to 30MHz, worst case.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 14, 2018, 07:29:51 pm
content: Actually, I modified my code a little bit. But there is still have same issue./* Includes ------------------------------------------------------------------*/#include "main.h"#include "stm32f7xx_hal.h"#include "spi.h"#include "gpio.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* Private variables ---------------------------------------------------------*/const uint8_t testdata_out=102; // DAC Analog voltage is set for 2Vconst uint8_t DAC_A_Write = 0x1; // DAC's A output is selectedconst uint8_t DAC_B_Write = 0x5; // DAC's B output is selecteduint16_t DACA_Buf;uint16_t DACB_Buf;/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);/* USER CODE BEGIN PFP *//* Private function prototypes -----------------------------------------------*///void shiftOut(uint8_t * data, uint16_t size);/* USER CODE END PFP *//* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.** @retval None*/int main(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration----------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI6_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///for(i=0; i<8; i++)//{//shiftOut(dataOut+i,1);//HAL_Delay(30);//}// Latch or SYNC Pin needs to LOWHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_RESET);// Write data with SPIDACA_Buf = DAC_A_Write<<12 | testdata_out<<4;HAL_SPI_Transmit(&hspi6,(uint8_t *)DACA_Buf,16,100);HAL_Delay(100);// When data is ready, LATCH will highHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_RESET);// Write data with SPIDACB_Buf = DAC_B_Write<<12 | testdata_out<<4;HAL_SPI_Transmit(&hspi6,(uint8_t *)DACB_Buf,16,100);HAL_Delay(100);// When data is ready, LATCH will highHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_SET);// This code for controlling two analog switchesHAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);HAL_Delay(10);HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_9);HAL_Delay(10);}/* USER CODE END 3 */}/*** @brief System Clock Configuration* @retval None*/void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct;RCC_ClkInitTypeDef RCC_ClkInitStruct;/**Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/**Initializes the CPU, AHB and APB busses clocks*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = 16;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 8;RCC_OscInitStruct.PLL.PLLN = 216;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Activate the Over-Drive mode*/if (HAL_PWREx_EnableOverDrive() != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Initializes the CPU, AHB and APB busses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK){_Error_Handler(__FILE__, __LINE__);}/**Configure the Systick interrupt time*/HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);/**Configure the Systick*/HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @param file: The file name as string.* @param line: The line in file as a number.* @retval None*/void _Error_Handler(char *file, int line){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */while(1){}/* USER CODE END Error_Handler_Debug */}#ifdef USE_FULL_ASSERT/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/void assert_failed(uint8_t* file, uint32_t line){/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif /* USE_FULL_ASSERT *//*** @}*//*** @}*//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

author: newbrain
author_rank: Super Contributor
timestamp: September 14, 2018, 08:12:47 pm
content: [ code] please...Code:// Latch or SYNC Pin needs to LOWHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_RESET);// Write data with SPIDACA_Buf = DAC_A_Write<<12 | testdata_out<<4;HAL_SPI_Transmit(&hspi6,(uint8_t *)DACA_Buf,16,100);HAL_Delay(100);// When data is ready, LATCH will highHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_RESET);// Write data with SPIDACB_Buf = DAC_B_Write<<12 | testdata_out<<4;HAL_SPI_Transmit(&hspi6,(uint8_t *)DACB_Buf,16,100);HAL_Delay(100);// When data is ready, LATCH will highHAL_GPIO_WritePin(Latch_Pin_GPIO_Port, Latch_Pin_Pin, GPIO_PIN_SET);I see a number of problems, but I won't spoil the fun giving you the solutions straight awayYou have defined DACA_Buf and DACB_Buf as uint16_t, and formatted the data and control fields correctlyNow think how you are passing them to the HAL_SPI_Transmit() function:its 2nd parameter isuint8_t *pData, and you have used a cast, but...something is wrong, as you are casting an integer into a pointer. Is that what you really want?After you address that, remember that Cortex-M is a little endian platform but the SPI is MSB first out: do we need to do something more?An uint16_t is two byte long, how many bytes are you writing to the SPI?Minor, not an actual problem: The 100ms delays are really not needed, check tCSFRand tSYNCin the datasheet...There might be something else, but this should get you going.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 14, 2018, 09:58:05 pm
content: Thanks for your interest!I updated testdata_out array.uint8_t testdata_out={0,102,0,102,0,102,0,102,0,102,0,102,0,102,0,102}; // DAC Analog voltage is set for 2V

author: newbrain
author_rank: Super Contributor
timestamp: September 15, 2018, 09:50:23 am
content: Quote from: A.Ersoz on September 14, 2018, 09:58:05 pmFine, but did you address the points in my post?If yes, how?

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 15, 2018, 03:26:25 pm
content: Based on your respond, I understand that I should define an array. In addition to that I determined each bit as 0 Voltage and 2 Voltage bit value. I calculated 102 value with datasheet referencing. In the datasheet, Vout=Vin*(D/256).

author: newbrain
author_rank: Super Contributor
timestamp: September 15, 2018, 06:35:56 pm
content: Quote from: A.Ersoz on September 15, 2018, 03:26:25 pmAh, well...it seems I was maybe a bit too cryptic.Ok, let's take it step by step then!0. An array is for sure the right structure if you want to hold a series of values to be transmitted.That said, the problems with the code you posted are not solved by this, so let's just use a single value for the moment, your variables below are perfectly fine.Code:const uint8_t testdata_out=102; // DAC Analog voltage is set for 2Vconst uint8_t DAC_A_Write = 0x1; // DAC's A output is selectedconst uint8_t DAC_B_Write = 0x5; // DAC's B output is selecteduint16_t DACA_Buf;uint16_t DACB_Buf;1. I was complaining about the pointer paramter to the transmit call. You wrote:HAL_SPI_Transmit(&hspi6,(uint8_t *)DACA_Buf,16,100);What you are doing here is casting thecontentof DACA_Buf to an uint8_t*, whereas you need to pass itsaddressto the function. What is needed is (uint8_t*)&DACA_Buf2. But when we have done this, we might find another problem:Did you configure the SPI for 16 bits in CubeMX? If you did,disregard this point.If you configured it at 8 bits (the default IIRC), read the following.Cortex-M Arms are little endian machines.So DACA_Buf, that has been assigned 0x1660, will be stored in memory as 0x60, 0x16. The SPI will then transmit 0x6016: not what we need.The easy way to solve the issue is swapping the bytes:Code:DACA_Buf = (DACA_Buf>>8) | (DACA_Buf<<8);This, of course, before the HAL_SPI_Transmit() call!3. The next thing to address is that we want to transmit just 2 bytes in every transaction, not 16 as it's written in the code. As said, this DAC will ignore everything after the first 16 bits.HTH

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 16, 2018, 04:12:08 am
content: Thank you very much! Appreciated!I set 16 bits configuration in CubeMX in SPI Configuration part. Now, I got two errors which areDAC and Analog Switch Control 9 14 18 v1\DAC and Analog Switch Control 9 14 18 v1.axf: Error: L6218E: Undefined symbol SystemClock_Config (referred from main.o).DAC and Analog Switch Control 9 14 18 v1\DAC and Analog Switch Control 9 14 18 v1.axf: Error: L6218E: Undefined symbol _Error_Handler (referred from spi.o).Do you have any idea? I googled this errors, and I understand that these errors are related with wrong address issue. What do you think?

author: newbrain
author_rank: Super Contributor
timestamp: September 16, 2018, 01:39:14 pm
content: Quote from: A.Ersoz on September 16, 2018, 04:12:08 amWell, sometimes I do.Some idea is also related to the problem at handThe messages mean simply that the linker cannot find any function with those names.Look at the main.c code you included above, those two functions (automatically generated by CubeMx) are at the end of the file (the assert one is not used in general, as USE_FULL_ASSERT defaults to non-defined).You have probably botched an edit and somehow made them part of a comment or a false #if/#ifdef or something similar, so they are no longer part of the compiled code.Aren't you getting any warning before the linker error?

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 17, 2018, 03:14:16 pm
content: Thank you! I solved the problemMy another question is that the output of DAC is not same as calculated value of mine. I defined test_data as '102', so I expected to see 2V. But the output is mV level. What do you think?

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 17, 2018, 03:43:07 pm
content: Now, my lat question is also OK. I managed to problem with modifying baud rate of SPI.Now, there is an interesting point. I got output A of the DAC but output B does not generate signal.

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 17, 2018, 04:19:43 pm
content: I am sorry for making busy of blog. I also solved the last question.I modified DACA_Buf and DACB_Buf declaration like switching the parameters.DACA_Buf = DAC_A_Write<<4 | testdata_out<<12;Now, I got what expected signals.I hope that this conversation helps or provides a good example for SPI controlled external DAC.Thank you very much newbrain and other contributors!

author: newbrain
author_rank: Super Contributor
timestamp: September 23, 2018, 08:53:56 pm
content: Quote from: A.Ersoz on September 17, 2018, 04:19:43 pmMmmh...this is definitely not correct. Let's have a look:DAC_A_Write = 0x01testdata_out=102 = 0x66DAC_A_Buf = (0x01 <<4) | (0x66 << 12) = 0x10 | 0x66000 = 0x66010, truncated to 0x6010 as it is an uint16_t.By pure chance this command will write the value 0x01 to the output of both DACs, but that's not what you need...The other major point is that in the HAL_SPI_Transmit call the length is indicated as 16: this parameter is the number of bytes (if SPI is set for <=8bit) or words (> 8 bit) to transfer.From the code I got in PM the SPI is set for 16 bits, so this value should be1. No bit shuffling needed to correct endianness.Is the DAC correctly connected in the circuit? Is Vref at a correct value?

author: A.Ersoz
author_rank: Regular Contributor
timestamp: September 24, 2018, 03:43:21 pm
content: You mean DAC output A and DAC Output B definitions are not correct?

author: newbrain
author_rank: Super Contributor
timestamp: September 24, 2018, 06:17:27 pm
content: Quote from: A.Ersoz on September 24, 2018, 03:43:21 pmDAC_A_Write and DAC_B_Write are correctly defined!What is wrong is the shifting, that was instead correct in the original code:Code:DACB_Buf = DAC_B_Write<<12 | testdata_out<<4; // Correct, will be in binary 0b0101_0110_0110_0000DACA_Buf = DAC_A_Write<<4 | testdata_out<<12; // Wrong, will be in binary 0b0110_0000_0001_0000!