ff-ad9833 Repo

codeberg repository for ad9833-related FlashForth code

For my on-going exploration of ad9833 audio generation using FlashForth 5, a code repository is now available at codeberg:

https://codeberg.org/infrared/ff-ad9833

So far, I have a demo-pitches word that plays c4, e4, g4, and c5 frequencies, but I can set other frequencies ranging from a4 to g#6, using the words I have so far. There is no system yet for setting duration, so I can’t play notes proper. But it was fun setting up a system for setting the equal temperament pitches.

In microcontroller programming, it is much easier to justify efforts to save memory. I needed to store the precalculated frequency register data values for each pitch. Originally I had a table like so:

$5274 , $4000 ,
$538d , $4000 , 
$54b7 , $4000 ,
$55f2 , $4000 ,
...
$4b5a , $4002 ,

With each line (two 16 bits words) representing one pitch. However, the first two bits in each 16-bit word are actually not frequency data, but register addressing bits, which can easily be added in later. Also, the second 16-bit word in each pair has (in my application) only two bits of actual frequency data, which are the two right most bits. So, I cut the table memory usage in half by packing those two bits into the left-most two bits of the first word in each pair:

\ packaged freq register data for equal temp
\ pitches from A4, A#4, B4 , ... , G#6
\ format numbered from lsb to msb:
\ 0-13: the 14 LSBs of the lsb register load
\ 14-15: the 2 LSBs of the msb register load

create packed-pitches
$1274 , $138d ,
$14b7 , $15f2 ,
$1740 , $18a2 ,
$1a19 , $1ba7 ,
...

Now each line in the table is actually two separate pitches. Then I just need a few words to separate the bits back out into separate words, and recombine them with the frequency register addressing bits:

0  constant na
1  constant na#
2  constant nb
3  constant nc
4  constant nc#
5  constant nd
6  constant nd#
7  constant ne
8  constant nf
9  constant nf#
10 constant ng
11 constant ng#

0 constant o4
1 constant o5
2 constant o6

: pull-pitch ( note octave -- u ) 12 * + cells packed-pitches + @ ;

: pull-14lsb %0011111111111111 and ;

: pull-2msb %1100000000000000 and 14 rshift ;

%0100000000000000 constant FREG0

: tx-pitch ( note octave -- )
    pull-pitch cp>r pull-14lsb FREG0 or 2tx-spi
    r> pull-2msb FREG0 or 2tx-spi ;

In testing, I have some trouble with some pitches getting distorted when I set the volume to higher levels. I suspect the problem is that I am still using the USB power supply and need to feed in my +5V from an external PS. I plan to try that next week, God willing.

AD9833 Signal Driven by Flash Forth

400 Hz signal from an AD9833 module, initialized by 328P MC running FlashForth

Having figured out how to configure the parameters of the SPI bus, and transmit bytes, I wanted then drive my AD9833 module, which is a frequency generator.

Arduino Uno running FlashForth connected to a GY-9833 module, which is a dev module for the AD9833 signal generator chip

I determined from the datasheet that I would need the SCLK signal to be high on idle, and to sample on the leading edge. Also, fsync needs to go low before the data is transmitted.

Timing Diagrams for AD9833 chip

Here are the FSYNC, SCK, and data signals on a scope:

FSYNC signal is yellow, SCK signal is green, and data signal is purple

I actually had to study these signals for a few minutes because at first the initialization code was not working. I released that there was a bug in my code such that the fsync signal was inverted. That was easy to fix.

The above data translates to this example initialization data from the programmer’s guide:

Zooming in, you can see the first byte, 0x21:

First byte’s worth of signals

With sampling on leading edge of SCK (green signal) we have 00100001, i.e., 0x21.

Here is the code so far:

\ ad9833.fs

\ Copyright 2021 Christopher Howard

\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at

\     http://www.apache.org/licenses/LICENSE-2.0

\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.

ad9833
marker ad9833

: init-spi ( -- )
    \ set data direction bits
    DD_OUT DD_MOSI lshift
    DD_OUT DD_SCK  lshift or
    DD_OUT DD_SS   lshift or DDR_SPI mset
    \ Setup control register
    1           SPR0 lshift
    0           SPR1 lshift or \ fck/16
    CPHA_SMPLED CPHA lshift or
    CPOL_HIDLE  CPOL lshift or
    MSTR_MSTR   MSTR lshift or 
    DORD_MSB    DORD lshift or
    SPE_ENAB    SPE  lshift or
    SPIE_DISAB  SPIE lshift or SPCR c!
;

: init-fsync [ DD_OUT #4 lshift ] literal DDRD mset ;

: fsync-low [ 1 #4 lshift ] literal PORTD mclr ;

: fsync-high [ 1 #4 lshift ] literal PORTD mset ;

: demo-400hz
    init-spi
    init-fsync
    fsync-low
    $21 tx-spi $00 tx-spi
    $50 tx-spi $c7 tx-spi 
    $40 tx-spi $00 tx-spi 
    $c0 tx-spi $00 tx-spi 
    $20 tx-spi $00 tx-spi
    fsync-high
;

\ Only for viewing the SPI signals
: test-demo begin demo-400hz 1 ms again ;

First you have to load this ad9833 code:

\ ff-328p.fs

\ Copyright 2021 Christopher Howard

\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at

\     http://www.apache.org/licenses/LICENSE-2.0

\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.

ff-328p
marker ff-328p

\ General I/O port addresses

$25 constant PORTB
$28 constant PORTC
$2b constant PORTD

\ Data Direction Registers

$24 constant DDRB
  1 constant DD_OUT
  0 constant DD_IN
$2a constant DDRD

\ DDR for SPI comms

$24 constant DDR_SPI
$2 constant DD_SS
$3 constant DD_MOSI
$5 constant DD_SCK

\ SPI control register

$4c constant SPCR
$0 constant SPR0 \ SPI Clock Rate Selector bits
$1 constant SPR1 \ (see table 18-5 in 328P datasheet)
$2 constant CPHA \ Clock Phase bit
  1 constant CPHA_SMPTRL \ sample on trailing edge of SCK
  0 constant CPHA_SMPLED \ sample on leading edge of SCK
$3 constant CPOL \ Clock Polarity bit
  1 constant CPOL_HIDLE \ SCK high when idle
  0 constant CPOL_LIDLE \ SCK low when idle
$4 constant MSTR \ Master/Slave Select bit
  1 constant MSTR_MSTR \ master mode
  0 constant MSTR_SLAVE \ slave mode
$5 constant DORD \ Data Order bit
  1 constant DORD_LSB \ LSB transmitter first
  0 constant DORD_MSB \ MSB transmitted first
$6 constant SPE \ SPI Enable bit
  1 constant SPE_ENAB \ SPI enabled
  0 constant SPE_DISAB \ SPI disabled
$7 constant SPIE \ SPI Interrupt Enable bit
  1 constant SPIE_ENAB \ SPI Interrupt Enabled
  0 constant SPIE_DISAB \ SPI Interrupt Disabled

\ SPI status register

$4d constant SPSR 
$0 constant SPI2X \ Double SPI Speed Bit
$6 constant WCOL \ Write COLlision Flag
$7 constant SPIF \ SPI Interrupt Flag

\ SPI Data Register (i/o port)

$4e constant SPDR

: tx-spi ( c -- )
    SPDR c! begin SPSR c@ 1 SPIF lshift and until
;