{"id":1963,"date":"2019-03-15T21:34:46","date_gmt":"2019-03-15T20:34:46","guid":{"rendered":"http:\/\/wildlab.org\/?p=1963"},"modified":"2019-05-24T22:15:26","modified_gmt":"2019-05-24T21:15:26","slug":"stm-programming-adc-and-true-dac","status":"publish","type":"post","link":"https:\/\/wildlab.org\/index.php\/2019\/03\/15\/stm-programming-adc-and-true-dac\/","title":{"rendered":"STM programming ADC and true DAC"},"content":{"rendered":"<h2>True DAC or PWM dac?!<\/h2>\n<p>Depends of which board we have. If chip is STM32f103c8, then there is no DAC on it, but we can mimic DAC by using PWM. If board has stm32f103vet6 on the other hand, it has DAC and we can\u00a0 use both channels as stereo.\u00a0 Please watch video first, then it will be clear why we need move SPI pins with AFIO_MAPR function to another place &#8211; both shares the same pins.<\/p>\n<p><iframe loading=\"lazy\" title=\"STM32 programming ADC and DAC in Keil\" width=\"474\" height=\"267\" src=\"https:\/\/www.youtube.com\/embed\/trKGSUqWFMk?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<pre class=\"lang:c++ decode:true \" title=\"adc_dac.c\">#include \"stm32f10x.h\"\r\n#include \"printMsg.h\"\r\n#include \"delayUs.h\"\r\n#include \"adc.h\"\r\n#include \"dac.h\"\r\n#include \"spi.h\"\r\n#include \"sample.h\"\r\n#include \"pwm_as_dac.h\"\r\n\r\nint main(void)\r\n{\r\n\tusart_1_enable();\r\n\ttimer2enablePWM();\r\n\tspiEnable();\r\n\tadcEnable(); \r\n\tdacEnable(); \r\n\t\/\/setModule(433.120,0); \/\/ (frequency in MHz, power in steps from 0 to 7, or 1.26 mW to 100 mW)\r\n\t\r\n\twhile(1) \r\n\t{\r\n\t\t\/\/adc();\r\n\t\t\r\n\t\tfor (int i=0;i&lt;raw;i++)\r\n\t\t{\r\n\t\t\tPWMdac(rawData[i]\/4,rawData[i]\/4); \/\/since our PWM DAC imitation has only 8 bit resolution, we need to divide 4096 \/ 4\r\n\t\t\tdac(rawData[i]*10-2048,rawData[i]*10-2048); \/\/8 bits to 12 bits resolution, but it may be needed to amplify by multiplying *10 and offset -2400\r\n\t\t\tdelay(330);\r\n\t\t}\r\n\t\tdac(left,right); \/\/normal DAC, if inputs are 12 bits, the output is also 12 bit\r\n\t\tdelay(50000000); \/\/long delay between two plays\r\n\t}\r\n\t\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"adc.c\">#include \"stm32f10x.h\"\r\n#include \"delayUs.h\"\r\n#include \"adc.h\"\r\n#include \"dac.h\"\r\n\r\nint left=0;\r\nint right=0;\r\n\r\nvoid adcEnable(void)\r\n{\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_ADC1EN |RCC_APB2ENR_ADC2EN | RCC_APB2ENR_AFIOEN; \/\/enabling ADC clock, interrupt enable, \r\n\tRCC-&gt;CFGR |= RCC_CFGR_ADCPRE_DIV6;\/\/ ADC clock = 12 MHz, maximum is 14, but there is no divider for that freq (72MHz \/ 6 = 12MHz). works with div6 too\r\n\t\/\/while clock for port A and B is enabled down below\r\n\t\/\/ pin B0 is analog input, no need special GPIO setting\r\n\t\/\/ pin B1 is analog input, no need special GPIO setting\r\n\t\r\n\tADC1-&gt;CR1 |=ADC_CR1_EOCIE;   \/\/ADC interrupt enabled\r\n\tADC2-&gt;CR1 |=ADC_CR1_EOCIE;   \/\/ADC interrupt enabled\r\n\t\/\/NVIC_EnableIRQ(ADC1_2_IRQn); \/\/interrupt enabled\r\n\t\r\n\tADC1-&gt;SMPR2 |= ADC_SMPR2_SMP8_0;\/\/|ADC_SMPR2_SMP8_1|ADC_SMPR2_SMP8_2;\r\n\tADC2-&gt;SMPR2 |= ADC_SMPR2_SMP8_0;\/\/|ADC_SMPR2_SMP8_1|ADC_SMPR2_SMP8_2;\r\n\r\n\tADC1-&gt;SQR3 |=8; \/\/L B0\r\n  ADC2-&gt;SQR3 |=9; \/\/R B1\r\n\tADC1-&gt;CR2 &amp;= ~ADC_CR2_ALIGN; \/\/data is right aligned (0bxxxx111111111111)\r\n\tADC2-&gt;CR2 &amp;= ~ADC_CR2_ALIGN; \r\n\r\n\tADC1-&gt;CR2 |= ADC_CR2_ADON | ADC_CR2_CONT; \/\/ADC converter is on\r\n\tADC2-&gt;CR2 |= ADC_CR2_ADON | ADC_CR2_CONT;\r\n\tdelay(1000); \/\/alow ADC to stabilize - 1 mS, but my delay is not exactly 1 mS, it is much shorter...\r\n  ADC1-&gt;CR2 |= ADC_CR2_CAL;\r\n\tADC2-&gt;CR2 |= ADC_CR2_CAL;\r\n\tdelay(1000); \/\/it is better to leave some time, just few clock cycles...\r\n\tADC1-&gt;CR2 |= ADC_CR2_ADON; \/\/not sure it requires to call it again?\r\n\tADC2-&gt;CR2 |= ADC_CR2_ADON; \r\n\tdelay(1000); \/\/After first ADON, ADC is just set, then second time ADC is actually enabled\r\n}\r\n\r\n\/* For enabling IRQ, uncomment three lines above *\/\r\nvoid ADC1_2_IRQHandler(void)\r\n{ \r\n    left=ADC1-&gt;DR;\r\n\t\tright=ADC2-&gt;DR;\r\n} \r\n\r\n\/* Manual call of the ADC conversion channels *\/\r\nvoid adc(void)\r\n{\r\n\tADC1-&gt;CR2 |=ADC_CR2_SWSTART;\r\n\tADC2-&gt;CR2 |=ADC_CR2_SWSTART;\r\n\tleft=ADC1-&gt;DR;\r\n  right=ADC2-&gt;DR;\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"adc.h\">#ifndef adc_h\r\n#define adc_h\r\n\r\nextern int left;\r\nextern int right;\r\n\r\nvoid adcEnable(void);\r\nvoid adc(void);\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"dac.c\">#include \"stm32f10x.h\"\r\n#include \"printMsg.h\"\r\n#include \"delayUs.h\"\r\n\r\nvoid dacEnable(void)\r\n{\r\n\t\r\n\tRCC-&gt;CFGR |= RCC_CFGR_PPRE1_DIV2;\/\/ trying to divide clock by 2 for APB1, for max freq of 36 MHz\r\n\tRCC-&gt;APB1ENR |= RCC_APB1ENR_DACEN; \/\/DAC interface clock enable\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPAEN;\r\n\t\t\r\n\tDAC-&gt;CR |= DAC_CR_BOFF1|DAC_CR_BOFF2; \/\/DAC buffer\r\n\t\/\/DAC-&gt;CR |= DAC_CR_WAVE2_1|DAC_CR_WAVE2_0|DAC_CR_WAVE1_1|DAC_CR_WAVE1_0;\r\n\t\/\/DAC-&gt;CR |= DAC_CR_MAMP1_3|DAC_CR_MAMP2_1|DAC_CR_MAMP1_1|DAC_CR_MAMP1_0; \/\/maximum aplitude?\r\n\t\/\/DAC-&gt;CR |= DAC_CR_MAMP2_3|DAC_CR_MAMP2_2|DAC_CR_MAMP2_1|DAC_CR_MAMP2_0; \/\/maximum aplitude?\r\n\tDAC-&gt;CR |= DAC_CR_TSEL1_0|DAC_CR_TSEL1_1|DAC_CR_TSEL1_2; \/\/software trigger 0b111\r\n  DAC-&gt;CR |= DAC_CR_TSEL2_0|DAC_CR_TSEL2_1|DAC_CR_TSEL2_2; \/\/software trigger 0b111\r\n\tDAC-&gt;CR |= DAC_CR_TEN1|DAC_CR_EN1; \/\/software trigger for DAC1 enable, DAC1 enable\r\n\tDAC-&gt;CR |= DAC_CR_TEN2|DAC_CR_EN2; \/\/software trigger for DAC2 enable, DAC2 enable\r\n}\r\n\r\nvoid dac(int left, int right)\r\n{\r\n\tDAC-&gt;SWTRIGR |= DAC_SWTRIGR_SWTRIG1;\r\n\tDAC-&gt;SWTRIGR |= DAC_SWTRIGR_SWTRIG2; \/\/not sure which trigger, so both...\r\n\tDAC-&gt;DHR12R1 = left; \/\/currently left channel, pin A4\r\n\tDAC-&gt;DHR12R2 = right; \/\/currently right channel, pin A5\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"dac.h\">#ifndef dac_h\r\n#define dac_h\r\n\r\nextern int audio;\r\nvoid dacEnable(void);\r\nvoid dac(int left,int right);\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"pwm_as_dac.c\">#include \"stm32f10x.h\"\r\n#include \"adc.h\"\r\n\r\nvoid timer2enablePWM(void)\r\n{\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; \/\/port B clock enabled (3), port A clock enable (2), Alternate IO clock enable (0)\r\n\tRCC-&gt;APB1ENR |= RCC_APB1ENR_TIM2EN; \/\/timer 2 clock enable (2)\r\n\t\r\n\tGPIOA-&gt;CRL |= GPIO_CRL_CNF0_1|GPIO_CRL_MODE0_0|GPIO_CRL_MODE0_1;\r\n\tGPIOA-&gt;CRL &amp;= ~(GPIO_CRL_CNF0_0);  \/\/PA0\r\n\t\r\n  GPIOA-&gt;CRL |= GPIO_CRL_CNF1_1|GPIO_CRL_MODE1_0|GPIO_CRL_MODE1_1;\r\n\tGPIOA-&gt;CRL &amp;= ~(GPIO_CRL_CNF1_0); \/\/PA1\r\n\t\r\n  TIM2-&gt;CCER |= TIM_CCER_CC1E; \/\/capture\/compare timer1 output enable\r\n\tTIM2-&gt;CCER |= TIM_CCER_CC2E; \/\/capture\/compare timer2 output enable\r\n\t\r\n\tTIM2-&gt;CR1 |= TIM_CR1_ARPE; \/\/auto reload preload enable TIMxARR is buffered\r\n\tTIM2-&gt;CCMR1 |= TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE; \/\/output compare 2 mode 0b110 (14:12)?, output compare 2 preload enable\r\n\tTIM2-&gt;CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE;\r\n\t\r\n\t\r\n\t\/\/PWM freq = Fclk\/PSC\/ARR  72MHz\/1000\r\n\t\/\/PWM Duty = CCR1\/ARR\r\n\tTIM2-&gt;PSC = 0; \/\/prescaler value, 72 MHz divided by:\r\n\tTIM2-&gt;ARR = 1024; \/\/auto reload register, value of 1024 with prescaler value 0 result in PWM frequency of 70 kHz\r\n\t\/\/TIM2-&gt;CCR1= 512; \/\/first of two channels\r\n\t\/\/TIM2-&gt;CCR2= 512; \/\/capture\/compare value, duty cycle (disabled here, enabling in call function in DSP_1.c)\r\n\tTIM2-&gt;EGR |= TIM_EGR_UG; \/\/update generation, re-initialize\r\n\tTIM2-&gt;CR1 |= TIM_CR1_CEN; \/\/counter enabled\r\n}\r\n\r\nvoid PWMdac(int left, int right)\r\n{\r\n\tTIM2-&gt;CCR1 = left;  \/\/duty cycle\r\n\tTIM2-&gt;CCR2 = right; \/\/duty cycle\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"pwm_as_dac.h\">#ifndef pwm_as_dac_h\r\n#define pwm_as_dac_h\r\n\r\nvoid timer2enablePWM(void);\r\nvoid PWMdac(int left,int right);\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"spi.c\">\/* This software is provided by https:\/\/wildlab.org and\r\n   Milan Karakas from Croatia. This is free program, but \r\n\t also \"beerware\". This means if you want this beta test\r\n\t phase to grow into something really great, please consider \r\n\t some donation here: \r\n\t https:\/\/www.paypal.me\/milankarakas?locale.x=en_US\r\n\t It is still in beta test phase, and will be upgraded. \r\n\t So far it works at 433.120 MHz, and you need to change \r\n\t the frequency if you wish to something else - in library\r\n\t 'Si4432.c', there is math and you should to calculate \r\n\t and change values of the register for other frequencies. \r\n\t Out there exist also Si4432 - 868 MHz module, and this \r\n\t program works with this one too, but then you MUST change\r\n\t the frequency, else output TX amplifier may burn. \r\n\t Later, will ad my own math to do that, but since it is still\r\n\t in beta testing phase... stay tuned.\r\n*\/\r\n\r\n#include \"stm32f10x.h\"\r\n\r\nvoid spiEnable(void)\r\n{\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPBEN|RCC_APB2ENR_AFIOEN; \/\/already exist, but anyway...\r\n\t\/* Remapping SPI pins *\/\r\n\tAFIO-&gt;MAPR |=AFIO_MAPR_SWJ_CFG_1; \/\/first we should to enable afio clock (above), then this one \"JTAG-DP Disabled and SW-DP Enabled\", releasing PA4 and PA5\r\n\tAFIO-&gt;MAPR |=AFIO_MAPR_SWJ_CFG_0|AFIO_MAPR_SPI1_REMAP; \/\/moving SPI1 from PA4\/PA5\/PA6\/PA7 to PA15\/PB3\/PB4\/PB5 (PA15=NSS, PB3=SCK, PB4=MISO, PB5=MOSI\r\n\t\r\n\t\/* GPIO pin A15 (was A4) is NSS (\"Not\" Slave Sellect), inverted... if low (0), then it is selected *\/\r\n\tGPIOA-&gt;CRH |= GPIO_CRH_CNF15_1|GPIO_CRH_MODE15_0|GPIO_CRH_MODE15_1;\/\/ 50 MHz - NSS (nSEL on Si4432)\r\n\tGPIOA-&gt;CRH &amp;= ~GPIO_CRH_CNF15_0; \/\/alternate function PP, this A4-&gt;moved to A15 pin must be high with external resistor 3kOhm to 4.7kOhm\r\n\t\/* GPIO pin B3 (was A5) is SPI clock SCK *\/\r\n\tGPIOB-&gt;CRL |= GPIO_CRL_CNF3_1|GPIO_CRL_MODE3_0|GPIO_CRL_MODE3_1;\/\/ 50 mhz - SCK A5-&gt;moved to B3\r\n\tGPIOB-&gt;CRL &amp;= ~GPIO_CRL_CNF3_0; \/\/alternate function PP\r\n\t\/* GPIO pin B4 (was A6) is MISO *\/\r\n\tGPIOB-&gt;CRL &amp;= ~(GPIO_CRL_MODE4_0|GPIO_CRL_MODE4_1); \/\/ INPUT - MISO, A6-&gt;moved to B4\r\n\tGPIOB-&gt;CRL |= GPIO_CRL_CNF4_0;\r\n\t\t\r\n\t\/* GPIO pin B5 (was A7) is MOSI *\/\r\n\tGPIOB-&gt;CRL |= GPIO_CRL_CNF5_1|GPIO_CRL_MODE5_0|GPIO_CRL_MODE5_1;\/\/ 50 MHz - MOSI, A7-&gt;moved to B5\r\n\tGPIOB-&gt;CRL &amp;= ~GPIO_CRL_CNF5_0; \/\/alternate function PP\r\n\t\r\n\t\/*  Configuring SPI *\/\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_SPI1EN;\/\/ SPI enable\r\n\tSPI1-&gt;CR1 |= SPI_CR1_BR_2;\/\/|SPI_CR1_BR_1;\/\/|SPI_CR1_BR_1|SPI_CR1_BR_0; \/\/ 72MHz\/?\r\n\tSPI1-&gt;CR1 &amp;= ~(SPI_CR1_CPOL|SPI_CR1_CPHA|SPI_CR1_DFF); \/\/ modes, 0 for polarity, and 0 for iddle clock=0, dff=0 (8 bit), lsbfirst=0 (MSB first), *\r\n\tSPI1-&gt;CR2 |= SPI_CR2_SSOE; \/\/NSS enabled         \r\n\tSPI1-&gt;CR1 |= SPI_CR1_MSTR;\/\/ master configuration  \r\n}\r\n\r\nvoid spiWrite(int reg, int cmd)\r\n{\r\n  SPI1-&gt;CR1 |= SPI_CR1_SPE;\r\n\tSPI1-&gt;DR = (0x80|reg); \/\/usually 0x80 does not belongs to SPI, but here it is \"command mode\", specific for Si4432\r\n\twhile (!(SPI1-&gt;SR &amp; SPI_SR_TXE)){}; \r\n\tSPI1-&gt;DR = cmd; \r\n\twhile (!(SPI1-&gt;SR &amp;SPI_SR_TXE)){}; \r\n\twhile(SPI1-&gt;SR &amp; SPI_SR_BSY);\r\n\twhile (!(SPI1-&gt;SR &amp; SPI_SR_OVR)){}; \/\/if single byte is sent,...\r\n  SPI1-&gt;CR1 &amp;= ~SPI_CR1_SPE;\r\n}\r\n\r\nint spiRead(int reg)\r\n{\r\n\tSPI1-&gt;CR1 |= SPI_CR1_SPE;\r\n\tSPI1-&gt;DR =reg;\r\n\twhile (!(SPI1-&gt;SR &amp; SPI_SR_TXE)){};\r\n\twhile(SPI1-&gt;SR &amp; SPI_SR_BSY){}; \/\/waiting little bit longer between two sendings\r\n  SPI1-&gt;DR = 0xFF; \/\/dummy byte\r\n\twhile (!(SPI1-&gt;SR &amp; SPI_SR_RXNE)){};\r\n\tint rd=SPI1-&gt;DR; \/\/dummy read? Whole thing does not working properly without\r\n\twhile (!(SPI1-&gt;SR &amp; SPI_SR_RXNE)){};\r\n  int r=SPI1-&gt;DR;\r\n\tSPI1-&gt;CR1 &amp;= ~SPI_CR1_SPE;\r\n\treturn r;\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true\" title=\"spi.h\">#ifndef spi_h\r\n#define spi_h\r\n\r\nextern void spiEnable(void);\r\nextern void spiWrite(int reg, int cmd);\r\nextern int spiRead(int reg);\r\n\r\n#endif\r\n<\/pre>\n<p>Sorry, but last file &#8220;sample.h&#8221; is too long for this page, so it is in separate window (just select all, copy and paste into your sample.h file:\u00a0 <a href=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/sample.h\">sample<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>True DAC or PWM dac?! Depends of which board we have. If chip is STM32f103c8, then there is no DAC on it, but we can mimic DAC by using PWM. If board has stm32f103vet6 on the other hand, it has DAC and we can\u00a0 use both channels as stereo.\u00a0 Please watch video first, then it &hellip; <a href=\"https:\/\/wildlab.org\/index.php\/2019\/03\/15\/stm-programming-adc-and-true-dac\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">STM programming ADC and true DAC<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1966,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1963"}],"collection":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/comments?post=1963"}],"version-history":[{"count":8,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1963\/revisions"}],"predecessor-version":[{"id":2173,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1963\/revisions\/2173"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media\/1966"}],"wp:attachment":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media?parent=1963"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/categories?post=1963"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/tags?post=1963"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}