{"id":1910,"date":"2019-03-12T06:33:23","date_gmt":"2019-03-12T05:33:23","guid":{"rendered":"http:\/\/wildlab.org\/?p=1910"},"modified":"2019-05-24T22:14:24","modified_gmt":"2019-05-24T21:14:24","slug":"stm32-example-of-dsp-adc-and-dac","status":"publish","type":"post","link":"https:\/\/wildlab.org\/index.php\/2019\/03\/12\/stm32-example-of-dsp-adc-and-dac\/","title":{"rendered":"STM32 example of DSP, ADC and DAC"},"content":{"rendered":"<h2>DSP possible on small MCU?<\/h2>\n<p>Yes, DSP (Digital Signal Processing) is possible with some speed limitations. For example, if FIR filter (Finite Impulse Response) has too much taps, whole loop process will be slow, and sampling ratio depends strongly of number of those elements. Out there exist specialized MCUs with additional hardware for floating point calculation (FPU), but our STM 32 or whatever MCU you are using, can do DSP.<\/p>\n<p><iframe loading=\"lazy\" title=\"STM32 example of DSP ADC and DAC\" width=\"474\" height=\"267\" src=\"https:\/\/www.youtube.com\/embed\/CgXXKKTTlWs?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<p>First, we need to find some math to calculate &#8216;taps&#8217;, you may use your GNU radio companion for that, or some free online calculators as is this one (really simple): http:\/\/t-filter.engineerjs.com\/<\/p>\n<p>Just set parameter of wanted frequency filtering, and on the right side you have two choices: plain text or C\/C++ code. Chose code and copy\/paste into my codes in &#8216;coefficients.h&#8217; file, replacing old ones (or just comment old one with &#8216;\/\/&#8217;. Change &#8220;static double filter_taps[FILTER_TAP_NUM] = {&#8230;&#8221; into &#8220;static const float taps[] = {&#8230;.&#8221;, that is because it is intended for PC and other high frequency CPUs instead our MCUs. Name in &#8216;[]&#8217; square brackets is defined above by &#8220;#define FILTER_TAP_NUM&#8221;, so leave those brackets empty, else compiler may complain about re-definition.<\/p>\n<h2>The codes:<\/h2>\n<pre class=\"lang:c++ decode:true\" title=\"DSP_1.c\">#include \"stm32f10x.h\"\r\n#include \"coefficients.h\"\r\n#include \"pwm_as_dac.h\"\r\n#include \"adc.h\"\r\n\r\nfloat y=0.0;\r\nint x[FILTER_TAP_NUM ];\r\nint adc_value=0;\r\n\r\nint main(void)\r\n{\r\n\tADCenable();\r\n\ttimer2enablePWM();\r\n\r\n\twhile(1)\r\n\t{\r\n\t\ty=0;\r\n\t\tfor(uint16_t i=0;i&lt;FILTER_TAP_NUM ;i++)\r\n\t\t{\r\n\t\t\ty=y+x[i]*taps[FILTER_TAP_NUM  - i - 1];\r\n\t\t}\r\n\t\tfor(uint16_t i=0;i&lt;FILTER_TAP_NUM -1;i++)\r\n\t\t{\r\n\t\t\tx[i]= x[i+1];\r\n\t\t}\r\n\t\tx[FILTER_TAP_NUM -1]= adc();\r\n\r\n\t\tdac((int)y\/4-200);\r\n\t}\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"coefficients.h\">#ifndef coeficients_h\r\n#define coeficients_h\r\n\r\n\/\/ Define FILTER_TAP_NUM 33\t\r\n\/\/static const float h[] = {4.684409422081195e-18, -0.005663635209202766, 6.734740765645466e-18, 0.011587434448301792, -1.2573590333975574e-17, -0.02472740039229393, 2.1312048747716416e-17, 0.04784134402871132, -3.1619763082060183e-17, -0.08602967858314514, 4.1927475762042725e-17, 0.15215569734573364, -5.0665930867061117e-17, -0.2940477132797241, 5.650478043539123e-17, 0.9478252530097961, 1.502117395401001, 0.9478252530097961, 5.650478043539123e-17, -0.2940477132797241, -5.0665930867061117e-17, 0.15215569734573364, 4.1927475762042725e-17, -0.08602967858314514, -3.1619763082060183e-17, 0.04784134402871132, 2.1312048747716416e-17, -0.02472740039229393, -1.2573590333975574e-17, 0.011587434448301792, 6.734740765645466e-18, -0.005663635209202766, -4.684409422081195e-18};\t\r\n\t\r\n\/\/Define FILTER_TAP_NUM 33\t\r\n\/\/static const float h[] = {8.712546657581782e-25, -0.0002239293826278299, 8.553268804612164e-19, 0.0025619769003242254, -3.885060542008433e-18, -0.009687605313956738, 1.0061897762988933e-17, 0.026369733735919, -1.98794331381399e-17, -0.06051621213555336, 3.243698113131562e-17, 0.12741217017173767, -4.522882306991331e-17, -0.2757503390312195, 5.4873560415101953e-17, 0.9398812055587769, 1.4999059438705444, 0.9398812055587769, 5.4873560415101953e-17, -0.2757503092288971, -4.522882306991331e-17, 0.12741221487522125, 3.243699105748297e-17, -0.06051621213555336, -1.987942982941745e-17, 0.02636972814798355, 1.006189693580832e-17, -0.009687609039247036, -3.8850630235502704e-18, 0.002561978530138731, 8.553270355575813e-19, -0.000223929833737202, 8.712546657581782e-25};\t\r\n\r\n\t\r\n\/*\r\n\r\nFIR filter designed with\r\nhttp:\/\/t-filter.appspot.com\r\n\r\nsampling frequency: 10000 Hz\r\n\r\n* 0 Hz - 2700 Hz\r\n  gain = 1\r\n  desired ripple = 5 dB\r\n  actual ripple = 3.9012411972242793 dB\r\n\r\n* 3000 Hz - 5000 Hz\r\n  gain = 0\r\n  desired attenuation = -40 dB\r\n  actual attenuation = -40.57030203503276 dB\r\n\r\n*\/\r\n\r\n#define FILTER_TAP_NUM 35\r\n\r\nstatic const float taps[] = {\r\n  0.02414625111688885,\r\n  0.06401728741588443,\r\n  0.044933153881769605,\r\n  -0.015681240139849045,\r\n  -0.026149104235903296,\r\n  0.021462665496812958,\r\n  0.016109456391146398,\r\n  -0.02947270429340234,\r\n  -0.005886666419813914,\r\n  0.03886200531313775,\r\n  -0.010072111405837416,\r\n  -0.04736804280493483,\r\n  0.03610926485566697,\r\n  0.05442550241090494,\r\n  -0.08860251029391766,\r\n  -0.058971910009376544,\r\n  0.3122893416422745,\r\n  0.5605715308468502,\r\n  0.3122893416422745,\r\n  -0.058971910009376544,\r\n  -0.08860251029391766,\r\n  0.05442550241090494,\r\n  0.03610926485566697,\r\n  -0.04736804280493483,\r\n  -0.010072111405837416,\r\n  0.03886200531313775,\r\n  -0.005886666419813914,\r\n  -0.02947270429340234,\r\n  0.016109456391146398,\r\n  0.021462665496812958,\r\n  -0.026149104235903296,\r\n  -0.015681240139849045,\r\n  0.044933153881769605,\r\n  0.06401728741588443,\r\n  0.02414625111688885\r\n};\r\n\r\n\t\r\n#endif\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\r\nvoid ADCenable(void)\r\n{\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_ADC1EN | RCC_APB2ENR_AFIOEN; \/\/enabling ADC clock, interrupt enable, \r\n\tRCC-&gt;CFGR |= RCC_CFGR_ADCPRE_DIV4;\/\/ 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\tGPIOB-&gt;CRL &amp;= ~(GPIO_CRL_CNF0_0|GPIO_CRL_CNF0_1|GPIO_CRL_MODE0_0|GPIO_CRL_MODE0_1); \/\/ pin A0 is analog input\r\n\t\r\n\r\n\t\/\/ADC1-&gt;CR1 |=ADC_CR1_EOCIE;   \/\/ADC interrupt enabled\r\n\t\/\/NVIC_EnableIRQ(ADC1_2_IRQn); \/\/interrupt enabled\r\n\t\r\n\t\/\/ADC1-&gt;SMPR2 |= ADC_SMPR2_SMP8_0;\/\/|ADC_SMPR2_SMP8_1|ADC_SMPR2_SMP8_2;\r\n\tADC1-&gt;SQR3 |= ADC_SQR3_SQ1_3; \/\/for B0 in sequence 1, channel 8, it is 0b1000 = 8 (IN8)\r\n\t\/\/ADC1-&gt;SQR3 |=8; \/\/ alternative way of setting the same thing as above\r\n\r\n\tADC1-&gt;CR2 &amp;= ~ADC_CR2_ALIGN; \/\/data is right aligned (0bxxxx111111111111)\r\n\r\n\tADC1-&gt;CR2 |= ADC_CR2_ADON | ADC_CR2_CONT; \/\/ADC converter is on\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\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\tdelay(1000); \/\/After first ADON, ADC is just set, then second time ADC is actually enabled\r\n}\r\n\r\n\/* For some unknown reason, DSP does not work if algorithm is included into IRQ handler, so NVIC is disabled for this IRQ *\/\r\nvoid ADC1_2_IRQHandler(void)\r\n{ \r\n\t  if (ADC1-&gt;SR &amp; ADC_SR_EOC) \r\n\t{\r\n    adc_value=ADC1-&gt;DR;\r\n  }\t\r\n} \r\n\r\n\r\nint adc(void)\r\n{\r\n\tint adc=0;\r\n\tADC1-&gt;CR2 |=ADC_CR2_SWSTART;\r\n\t\/\/if(ADC1-&gt;SR &amp; ADC_SR_EOC)\r\n\twhile(!(ADC1-&gt;SR &amp; ADC_SR_EOC));\r\n\tadc=ADC1-&gt;DR;\r\n\treturn adc;\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 adc_value;\r\n\r\nvoid ADCenable(void);\r\nint adc(void);\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\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_CNF1_1|GPIO_CRL_MODE1_0|GPIO_CRL_MODE1_1;\r\n\tGPIOA-&gt;CRL &amp;= ~(GPIO_CRL_CNF1_0);\r\n\t\r\n  TIM2-&gt;CCER |= TIM_CCER_CC2E; \/\/capture\/compare timer2 output enable\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\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;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 dac(int value)\r\n{\r\n\tTIM2-&gt;CCR2 = value; \/\/here we update capture\/compare value for 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 dac(int value);\r\n\r\n#endif\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>DSP possible on small MCU? Yes, DSP (Digital Signal Processing) is possible with some speed limitations. For example, if FIR filter (Finite Impulse Response) has too much taps, whole loop process will be slow, and sampling ratio depends strongly of number of those elements. Out there exist specialized MCUs with additional hardware for floating point &hellip; <a href=\"https:\/\/wildlab.org\/index.php\/2019\/03\/12\/stm32-example-of-dsp-adc-and-dac\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">STM32 example of DSP, ADC and DAC<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"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\/1910"}],"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=1910"}],"version-history":[{"count":4,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1910\/revisions"}],"predecessor-version":[{"id":2172,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1910\/revisions\/2172"}],"wp:attachment":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media?parent=1910"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/categories?post=1910"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/tags?post=1910"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}