{"id":824,"date":"2012-01-05T20:56:20","date_gmt":"2012-01-06T01:56:20","guid":{"rendered":"http:\/\/abrammorphew.com\/notes\/?p=824"},"modified":"2017-04-11T14:21:19","modified_gmt":"2017-04-11T19:21:19","slug":"arduino-lfo-generator","status":"publish","type":"post","link":"http:\/\/abrammorphew.com\/notes\/2012\/01\/05\/arduino-lfo-generator\/","title":{"rendered":"Arduino: LFO Generator"},"content":{"rendered":"<p><a href=\"http:\/\/abrammorphew.com\/notes\/wp-content\/uploads\/2011\/12\/IMG_6703a.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"http:\/\/abrammorphew.com\/notes\/wp-content\/uploads\/2011\/12\/IMG_6703a.jpg\" alt=\"\" title=\"IMG_6703a\" class=\"alignnone size-full wp-image-826\" srcset=\"http:\/\/abrammorphew.com\/notes\/wp-content\/uploads\/2011\/12\/IMG_6703a.jpg 1024w, http:\/\/abrammorphew.com\/notes\/wp-content\/uploads\/2011\/12\/IMG_6703a-300x225.jpg 300w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>here&#8217;s a treat for anyone that&#8217;s into the audio side of arduino. it&#8217;s an 8-bit two-timer based LFO Generator using timer 0\/2 on an ATMega328p. i&#8217;m only using timer0 for output in this code. i&#8217;ve started implementing the LFO code into my 8-bit melotronium where timer2 is dedicated for the audio output. in the larger code base, timer0 just stores values in an unsigned integer that the other timer grabs and modulates the output mathematically. for now, this should be a good reference to anyone looking for the outline of an LFO with seven different wave forms. <\/p>\n<pre class=\"brush:cpp\">\r\n#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))\r\n#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))\r\n\r\nuint8_t sineTable[] = {\r\n  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,\r\n  192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,\r\n  239,240,242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,\r\n  254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,\r\n  233,231,229,227,225,223,221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,\r\n  181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,\r\n  115,111,108,105,102,99,96,93,90,87,84,81,78,76,73,70,67,64,62,59,56,54,51,49,46,44,\r\n  42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,\r\n  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31, 33,35,37,39,42,\r\n  44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124\r\n};\r\n\r\nuint8_t tWave = 128;\r\nuint8_t sWave = 255;\r\nuint8_t ruWave = 128;\r\nuint8_t rdWave = 128;\r\nuint8_t rWave = 128;\r\n\r\nint   i = 0;\r\nint   rate;\r\nint   waveform;\r\nbyte  d = HIGH;\r\n\r\nvoid setup() {\r\n  pinMode(3, OUTPUT);\r\n  setupTimer();\r\n  OCR0A = 128;\r\n}\r\n\r\nvoid loop() {\r\n  waveform = map(analogRead(0),0,1023,1,7);\r\n  rate = map(analogRead(1),0,1023,255,0);\r\n  OCR0A = rate;\r\n}\r\n\r\nISR(TIMER0_COMPA_vect) {\r\n  if(i == 255) i = 0;\r\n  switch(waveform) {\r\n    case 1:\r\n      analogWrite(3,sine(i));\r\n    break;\r\n    case 2:\r\n      analogWrite(3,triangle(i));\r\n    break;\r\n    case 3:\r\n      analogWrite(3, square(i));\r\n    break;\r\n    case 4:\r\n      analogWrite(3, rampUp(i));\r\n    break;\r\n    case 5:\r\n      analogWrite(3, rampDown(i));\r\n    break;\r\n    case 6:\r\n      analogWrite(3, rand(i));\r\n    break;\r\n    case 7:\r\n      analogWrite(3, white(i));\r\n    break;\r\n  }\r\n  i++;\r\n}\r\n\r\nvoid setupTimer() {\r\n  cli();\r\n\/*--- TIMER2 CONFIG ---*\/\r\n  sbi(TCCR2A,WGM20);\r\n  sbi(TCCR2A,WGM21);\r\n  cbi(TCCR2A,WGM22);\r\n \r\n  sbi(TCCR2B, CS20);\r\n  cbi(TCCR2B, CS21);\r\n  cbi(TCCR2B, CS22);\r\n\r\n  sbi(TCCR2A,COM2B1);\r\n  cbi(TCCR2A,COM2B0);\r\n   \r\n \/*--- TIMER0 CONFIG ---*\/ \r\n  cbi(TCCR0B,CS00);\r\n  cbi(TCCR0B,CS01);\r\n  sbi(TCCR0B,CS02);\r\n \r\n  sbi(TCCR0A, COM0A1);\r\n  cbi(TCCR0A, COM0A0);\r\n    \r\n  cbi(TCCR0A, WGM00);\r\n  sbi(TCCR0A, WGM01);\r\n  cbi(TCCR0A, WGM02);\r\n\r\n  cbi(TIFR0,OCF0A);\r\n  sbi(TIMSK0,OCIE0A);\r\n  sei(); \r\n}\r\n\r\nint sine(int i) {\r\n  return sineTable[i];\r\n}\r\n\r\nint triangle(int i) {\r\n  if(tWave >= 255) d = LOW;\r\n  if(tWave <= 0) d = HIGH;\r\n  if(d == HIGH) tWave++;\r\n  if(d == LOW) tWave--;\r\n  return tWave; \r\n}\r\n\r\nint rampUp(int i) {\r\n  ruWave++;\r\n  if(ruWave > 255) ruWave = 0; \r\n  return ruWave;\r\n}\r\n\r\nint rampDown(int i) {\r\n  rdWave--;\r\n  if(rdWave < 0) rdWave = 255;\r\n  return rdWave;\r\n}\r\n\r\nint square(int i) {\r\n  if(i >= 128) sWave = 255;\r\n  if(i <= 127) sWave = 0;\r\n  return sWave;\r\n}\r\n\r\nint rand(int i) {\r\n  if(i == rWave) rWave = random(255);\r\n  return rWave;\r\n}\r\n\r\nint white(int i) {\r\n  return random(255);\r\n}\r\n<\/pre>\n<p>Update: Here's my current code for the ATTiny85. It's not exactly bug-free as there seems to be some issue with latch-up when the speed of the LFO is high. This of course could be remedied by limiting the map() function's minimum value. I can't remember off hand if there are any other issues. Good power supply and output filtering is definitely needed to reduce noise in any additional stages of the circuit. At some point, I'll just make a separate post detailing my tremolo effect circuit in detail (so many projects and so little time!). Until then: <\/p>\n<pre class=\"brush:cpp\">\r\n\/*\r\n * Title: LFO Generator for ATTiny85 v0.12\r\n * Author: Abram Morphew\r\n * Date: 09.19.2016\r\n * Purpose: Low frequency oscillator with wave output controls\r\n *\/\r\n\r\n#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))\r\n#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))\r\n\r\nuint8_t sineTable[] = {\r\n  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,\r\n  192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,\r\n  239,240,242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,\r\n  254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,\r\n  233,231,229,227,225,223,221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,\r\n  181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,\r\n  115,111,108,105,102,99,96,93,90,87,84,81,78,76,73,70,67,64,62,59,56,54,51,49,46,44,\r\n  42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,\r\n  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31, 33,35,37,39,42,\r\n  44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124\r\n};\r\n\r\nuint8_t tWave = 128;\r\nuint8_t sWave = 255;\r\nuint8_t ruWave = 128;\r\nuint8_t rdWave = 128;\r\nuint8_t rWave = 128;\r\n\r\nint   i = 0;\r\nint   rate;\r\nint   waveform;\r\nbyte  d = HIGH;\r\nconst uint8_t output = 1;                       \/\/ output pin #6 on ATTiny85\r\n\r\nvoid setup() {\r\n  setupTimer();\r\n  pinMode(3, INPUT);\r\n  pinMode(2, INPUT);\r\n  pinMode(output, OUTPUT);\r\n  OCR0A = 128;\r\n}\r\n\r\nvoid loop() {\r\n  waveform = map(analogRead(2),0,1023,1,6);\r\n  rate = map(analogRead(3),0,1023,255,0);\r\n  OCR0A = rate;\r\n}\r\n\r\nISR(TIMER0_COMPA_vect) {\r\n  if(i == 255) i = 0;\r\n  switch(waveform) {\r\n    case 1:\r\n      analogWrite(output,sine(i));\r\n    break;\r\n    case 2:\r\n      analogWrite(output,triangle(i));\r\n    break;\r\n    case 3:\r\n      analogWrite(output, square(i));\r\n    break;\r\n    case 4:\r\n      analogWrite(output, rampUp(i));\r\n    break;\r\n    case 5:\r\n      analogWrite(output, rampDown(i));\r\n    break;\r\n    case 6:\r\n      analogWrite(output, rand(i));\r\n    break;\r\n    case 7:\r\n      analogWrite(output, white(i));\r\n    break;\r\n  }\r\n  i++;\r\n}\r\n\r\nvoid setupTimer() {\r\n  cli(); \r\n \/*--- TIMER0 CONFIG ---*\/  \r\n  TCCR0A = 0b10000011;\r\n  TCCR0B = 0b00001010;    \/\/ last 3 bits set prescalar for Timer0\r\n\r\n  cbi(TIFR,OCF0A);\r\n  sbi(TIMSK,OCIE0A);\r\n  sei(); \r\n}\r\n\r\nint sine(int i) {\r\n  return sineTable[i];\r\n}\r\n\r\nint triangle(int i) {\r\n  if(tWave >= 255) d = LOW;\r\n  if(tWave <= 0) d = HIGH;\r\n  if(d == HIGH) tWave++;\r\n  if(d == LOW) tWave--;\r\n  return tWave; \r\n}\r\n\r\nint rampUp(int i) {\r\n  ruWave++;\r\n  if(ruWave > 255) ruWave = 0; \r\n  return ruWave;\r\n}\r\n\r\nint rampDown(int i) {\r\n  rdWave--;\r\n  if(rdWave < 0) rdWave = 255;\r\n  return rdWave;\r\n}\r\n\r\nint square(int i) {\r\n  if(i >= 128) sWave = 255;\r\n  if(i <= 127) sWave = 0;\r\n  return sWave;\r\n}\r\n\r\nint rand(int i) {\r\n  if(i == rWave) rWave = random(255);\r\n  return rWave;\r\n}\r\n\r\nint white(int i) {\r\n  return random(255);\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>here&#8217;s a treat for anyone that&#8217;s into the audio side of arduino. it&#8217;s an 8-bit two-timer based LFO Generator using timer 0\/2 on an ATMega328p. i&#8217;m only using timer0 for output in this code. i&#8217;ve started implementing the LFO code into my 8-bit melotronium where timer2 is dedicated for the audio output. in the larger [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[117,102,103,288,116,115,113,114],"class_list":["post-824","post","type-post","status-publish","format-standard","hentry","category-notes","tag-amtel","tag-arduino","tag-atmega328","tag-attiny85","tag-avr","tag-c","tag-lfo","tag-timer0"],"_links":{"self":[{"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/posts\/824","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/comments?post=824"}],"version-history":[{"count":10,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/posts\/824\/revisions"}],"predecessor-version":[{"id":1342,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/posts\/824\/revisions\/1342"}],"wp:attachment":[{"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/media?parent=824"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/categories?post=824"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/abrammorphew.com\/notes\/wp-json\/wp\/v2\/tags?post=824"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}