Cheap dds based on attiny13a, ad9850, hd44780 - Page 1
Forum section: electronics / projects

author: jimon
author_rank: Regular Contributor
timestamp: January 24, 2015, 08:01:27 pm
content: Newbie here, I've had cheap ad9850 board from ebay, and decided to do "1 evening" project to create dds signal generator unit from it.I've also had attiny13a and hd44780 and potentiometer, so this is what I've used.You can checkout code here :https://github.com/jimon/attiny13a_dds/blob/master/attiny13a_dds.cAnd checkout videohere!Main trouble is amount of flash memory in MCU, 1 kb is just to small for bit banging of two separate devices + ADC + logicMy first try was to use libraries from the web, but DDS lib had double division inside, whichs turnouted to be ~500 bytes of avr instructionssame goes to some lcd lib'sIn the end I end up rewriting code almost from scratch, doing complex optimization here and there, and pushing whole project in 1020 bytes binaryMost interesting part from codebase is how I generate control world for ad9850 :Code:const double ref_k = 4294967296.0 / DDS_EX_CLK;const uint64_t ref_kd_hz = ref_k * (1ULL << 32) * 1;const uint64_t ref_kd_khz = ref_k * (1ULL << 32) * 1000;const uint64_t ref_kd_mhz = ref_k * (1ULL << 32) * 10000;uint64_t control_world_big = (uint64_t)freq;if(mode)control_world_big *= ref_kd_khz;elsecontrol_world_big *= ref_kd_hz;uint32_t control_world_small = ((uint32_t*)&control_world_big);A trick is that 4294967296.0 / DDS_EX_CLK = 34.359738368. And if you multiply it by 10e9 then you get 34359738368, which is not a float value anymore. If you rebase 10e9 to binary you will get 2^30 = 1073741824.So you can multiply 34.359738368 by 2^30, then do calculation in integers and divide by 2^30 without loosing any precision. But if we use 2^32 instead then we can replace division with reading upper 4 bytes of 64 bit integerAs for features, two modes, 0-8160 KHz and 0-1020 Hz. You can switch between modes by turning to leftmost position.As for issues, I noticed that DDS board behave strange without load on outputs, so I've added 1k and 100 Ohm resistors, but if I add both of them then there are crosstalk between channels, haven't figure out how to solve this yet. Also there are tons of crosstalk if you route both outputs to same switch button. I haven't used reset pin to do stuff, because I don't have AVR HVSP, I'm currently using bus pirate v3 to do stuff.In the end I've spent 3 evenings already, and need to spend 1 more to build this on the protoboard instead.PS. Have you ever been so desperate with memory in MCU's ?

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2015, 08:14:54 pm
content: QuoteNot sure about that.There is a way to turn frequency into tuning words, using just integer math.Google it -> it will shrink your code size a lot.

author: Yansi
author_rank: Super Contributor
timestamp: January 24, 2015, 08:49:50 pm
content: Using double precision floating point division in a 8bit 1kByte MCU with 16bit wide instructions.. just wow :-) (This is why I hate arduino and all those so called libraries)If the 1kB is small, write it in assembly.Keep in mind, that AVR doesn't have divide instruction - compiler must replace it with more or less complicated algorithms.For AVR TINY stuff, they even don't have the multiply instruction!!

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2015, 08:53:31 pm
content: Quotei have had a 100% flash utilization case on a PIC.

author: jimon
author_rank: Regular Contributor
timestamp: January 24, 2015, 09:00:26 pm
content: Quote from: dannyf on January 24, 2015, 08:14:54 pmThis code only use integer math (64 bit math though). Can you recommend any simpler way ? AD9850 use 32 bit tuning word, so it's not really obvious to me how to make calculations in less then ~100 bytes (size of current solution), I could use fixed point floats, but srsly, it's never gonna be smaller then 100 bytes of code.PS. A reason why it's integer math is :Code:const double ref_k = 4294967296.0 / DDS_EX_CLK;const uint64_t ref_kd_hz = ref_k * (1ULL << 32) * 1;This part is calculated at compile time, so no double values in ram\binary.

author: Yansi
author_rank: Super Contributor
timestamp: January 24, 2015, 09:05:10 pm
content: OK, sorry, I've look there only for a short time. Overlooked these are const.Without the multiply, it won't probably never get less than that. Take one step bigger MCU, at least with an multiply instruction.

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 24, 2015, 09:28:57 pm
content: requesting attiny13 to do stuffs including lcd+gui is a recipe for disaster. in the linked video, whats the point of showing the mere freq when you have dso around? i used attiny13 with analog pots only to drive ad9850, 600+ bytes footprint code no problem...https://www.eevblog.com/forum/projects/ad9850-module-and-dds3x25-dds-based-fg-compared/?topicseen3 freq range based on pot position during startup1) auto: 1-8K Hz (400Hz step)2) audio: 10-44K Hz (2KHz step)3) general: 100-50M Hz (2MHz step)

author: jimon
author_rank: Regular Contributor
timestamp: January 24, 2015, 09:47:46 pm
content: Quote from: Mechatrommer on January 24, 2015, 09:28:57 pmDunno, just to challenge myself in long cozy Swedish evening ...Nice project thought.

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 24, 2015, 10:04:48 pm
content: Quote from: jimon on January 24, 2015, 09:47:46 pmchallenge is good i know that, but as i grow older i realize wasting time is not goodcheers.

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2015, 10:24:54 pm
content: Quotegoogle is your friend there,Your code compiles to 7608bytes flash / 266 bytes data (gcc-avr, debug mode); If I replace your routine (the section you highlighted) with the one I mentioned earlier, the same code compiles to 5050B flash / 10B data.Hope it helps.

author: Kalvin
author_rank: Super Contributor
timestamp: January 24, 2015, 10:26:14 pm
content: Quote from: Mechatrommer on January 24, 2015, 10:04:48 pmI think this project is not wasting of time at all. This is a good example that even using a resource constrained microcontroller combined with some clever coding, one can do quite a lot actually.

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2015, 10:36:54 pm
content: I agree. Displaying the frequency is a useful thing. Squeezing the most out of a 8pdip chip is simply fun.You may also consider frequency scanning, over frequency bands or in the IF bands (455/465K and 10.7Mhz).

author: jimon
author_rank: Regular Contributor
timestamp: January 24, 2015, 11:23:45 pm
content: Quote from: dannyf on January 24, 2015, 10:24:54 pmI would like to collaborate on this one. I've foundthis one(andthis onebut it's about 48 bit integer mul in PIC's), so a page tells :Code:Unsigned long tuning_word = (frequency * 4294967296LL) / DDS_CLOCK;oki, and my simplified code :Code:const double ref_k = 4294967296.0 / DDS_EX_CLK;const uint64_t ref_kd_hz = ref_k * (1ULL << 32) * 1;const uint64_t ref_kd_khz = ref_k * (1ULL << 32) * 1000;const uint64_t ref_kd_mhz = ref_k * (1ULL << 32) * 10000;uint64_t control_world_big = (uint64_t)freq;//if(mode)//control_world_big *= ref_kd_khz;//elsecontrol_world_big *= ref_kd_hz;uint32_t control_world_small = ((uint32_t*)&control_world_big);(I've commented out mode to make it fair).So with my code binary size : 12660 bytes elf, 986 bytes striped bin.If I replace with code above (note they also use 64 bit math), then results are : 12272 bytes elf, 1198 bytes striped bin (which is bigger then flash)Not a surprise though, because my code only use one std built-in function (__muldi3), and page code use two of them (__ashldi3 and __divdi3).PS. Did you mean something else ? Maybe I'm just bad at googling AVR topics.

author: dannyf
author_rank: Super Contributor
timestamp: January 24, 2015, 11:42:32 pm
content: It is difficult to find the right approach, as 99.99% of the ad9850 code out there is wrong in that they utilize inefficient code to convert the frequency to tuning words. Your approach is actually one of the better ones.What you are looking for is a generic implementation of freq * 2^N / F, where in this case, N = 32 and F=125Mhz. There are efficient ways to get it done, without resorting to floating point math or 64-bit integer math.

author: Yansi
author_rank: Super Contributor
timestamp: January 24, 2015, 11:43:48 pm
content: The frequency is directly proportional to the tuning word. Only single multiplication is needed.fout= M / n^2 * fclockYou just take the desired frequency, multiply that be a pre-calculated value, trim the low bytes as needed and use the rest.For example, fclockis 20 000 000 Hz, tuning word is 28 bit (268 435 456).Tuning word = K * frequency.Now solve the const K: 2^28 / 20 000 000 = 13.4217728Now you should know the way how to implement that...

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 25, 2015, 12:36:49 am
content: Quote from: Kalvin on January 24, 2015, 10:26:14 pmagree, but sometime i dont how people trained to jump to the conclusion/troll... i'm saying wasting time is not good... i'm not saying this project is wasting time.