This lab is to acquaint you with the peripheral setup on the C6211 DSK board for A/D and D/A conversion. After this lab you are expected to know how to set up McBSP1 port and the PCM3003 codec daughtercard.
Summary: This will acquaint you with the peripheral setup on the C6211 DSK board for A/D and D/A conversion.
This lab is to acquaint you with the peripheral setup on the C6211 DSK board for A/D and D/A conversion. After this lab you are expected to know how to set up McBSP1 port and the PCM3003 codec daughtercard.
The PCM3003 codec on the daughtercard is connected to the McBSP1 serial port of the C6211 CPU. Therefore, you need to set up the McBSP1 to be able to use the codec. After reset, the PCM3003 codec continuously perform A/D and D/A conversions. The configuration of the PCM3003 is changed only by hardware setting. The jumpers on the daughtercard can be set differently to change the setting. Currently, the jumpers are set for l6bit A/D and D/A at the fixed 48kHz sampling rate. The A/D conversion data are continuously dumped to the McBSP1 serial port. The D/A conversion data are continuously taken from the McBSP1 port.
To configure the McBSP1 serial port of the CPU, you need to store appropriate values to the registers controlling the serial port. These registers are memory mapped, and the physical addresses of the McBSP1 registers are
1 McBSP1_DRR 0x1900000 /* address of data receive reg.*/
2 McBSP1_DXR 0x1900004 /* address of data transmit reg.*/
3 McBSP1_SPCR 0x1900008 /* address of serial port contl. reg.*/
4 McBSP1_RCR 0xl90000C /* address of receive control reg.*/
5 McBSP1_XCR 0x1900010 /* address of transmit control reg.*/
6 McBSP1_SRGR 0x1900014 /* address of sample rate generator */
7 McBSP1_MCR 0x1900018 /* address of multichannel reg.*/
8 McBSP1_RCER 0xl9000lC /* address of receive channel enable.*/
9 McBSP1_XCER 0x1900020 /* address of transmit channel enable.*/
10 McBSP1_PCR 0x1900024 /* address of pin control reg.*/
The behavior of the McBSP1 is configured by setting up the SPCR, SRGR, PCR, RCR, and XCR registers. In the following labs, we set up the McBSP1 port so that both channels of A/D results can be read at the same time by reading a 32bit number from McBSP1. The upper and lower l6bits correspond to the left and right channels, respectively. Similarly, we set up the port output so that we can write a single 32bit number to McBSP1 for D/A conversion (again upper and lower l6bits correspond to left and right channels). For this set up, the following are the register values that need be set. Please read the McBSP registers description in the TI peripheral reference guide to understand why you should write the specified values.
Write an assembly routine that sets up the McBSP1. The above registers should be set (in the given order).
The DXR register contains the data to be output from the serial port. Once you write data to DXR, they are converted to serial format and output through the serial output pin, and then the PCM3003 codec receives the data. To avoid conflicts, you should write to DXR only after the McBSP finishes the transmission of the previously written data. Then, how can you know that the McBSP finished transmission of the data in DXR and is ready to take new data to transmit? This is done by checking the bit 17 (XRDY bit) of the SPCR register. When DXR is empty and McBSP is ready to take new data, the XRDY of the SPCR register becomes 1. Therefore, you can check this flag and when SPCR is empty, you can safely write to it to transmit the new data.
Assuming the 32-bit data to be transmitted through McBSP1 is in A0, write an assembly routine that checks the SPCR and then transmits the data to McBSP1. If DXR is not empty, your program must wait until McBSP1 finishes transmission of the remaining data.
Right after the port finishes transmitting the data, the XRDY of SPCR changes from 0 to 1 to indicate the termination of the transmission and that the port is ready to take new data. This event generates the interrupt XINT1. By enabling XINT1 and servicing this interrupt, you can avoid continuous polling of the port status.
The DRR register contains the data the McBSP port received from its data receive pin. After the PCM3003 codec chip finishes each A/D conversion, the result is sent to McBSP1 and stored in DRR. When the received data is available in DRR to be read by the CPU, the bit 1 (RRDY bit) of SPCR register becomes 1. Therefore, you can check this flag to see if there is any data available in DRR to be read. When the data becomes available, the serial port generates the RINT1 interrupt.
Write an assembly routine that checks the SPCR and then reads the data from DRR. If there is no data in DRR, your program must wait until McBSP1 receives new data into DRR.
Often you need to repeat exactly the same block of code with different parameters. The assembler macro is useful in this situation. For example, you can define a macro McBSP1_write to write a register value to McBSP1. The macro is defined in the following format.
1 McBSP1_write .macro r1
2 .....
3 ;assembly code writing r1 to McBSP1
4 loop? .....
5 .....
6 B loop?
7 .....
8 STW r1, *A10
9 .....
10 .....
11
12 .endm
1 McBSP1_write Al
However, you need to be careful when using labels insider macro. Because macro can be called repeatedly many times in a program, the same labels will be re-defined every time the macro is called. Because multiple definition of same label is not allowed, you have to append ? at the end of each label defined inside the macro (for example, loop? in the above). With this special labels, assembler can properly assign different label names for each different call of the macro.
Write macros to write a register to McBSP1 and read from McBSP1 to a register.
When you write an assembly program, it is often useful to have some temporary storage spaces to temporarily store and restore register values, etc. The stack is often used but C62x CPU does not explicitly support the stack operations. However, you can easily define a stack.
The first thing you need to do is to allocate a memory space as a stack. This is done in the linker command file by defining a section for the stack. For example, we can have
1 MEMORY
2 {
3 ...
4 IRAM: org = 0x00000240 len = 0x0000FCC0
5 STK: org = 0x0000FF00 len = 0x000000FF
6 ...
7 }
8
9 SECTIONS
10 {
11 ....
12 stack :> STK
13 }
1 ...
2 SP .set B15 ;use B15 as a stack pointer
3
4 .sect "stack"
5 endofstack: .bes 256
6
7 .text
8 MVKL endofstack—3, SP ;initially stack is empty
9 MVKH endofstack-3, SP ;and SP points to the bottom
10 ;word boundary
11
12 STW A0, *SP—-[1] ;this pushes A0 to stack
13
14 LDW *++SP[1], AO ;pull the last stored stack
15 ;data to A0
16 NOP 5
17 .endm
18 ...
The operation of the PCM3003 codec can be controlled only by hardware. In all the following labs, we will use the l6bit stereo input/output at the fixed 48kHz sampling. Therefore, do not modify the jumpers on the daughtercard. Later you may modify the jumpers to obtain desired sampling rate and other operations.
Now, you have all the necessary functions to read and write the serial port for A/D and D/A conversion. In the following experiment, you write an executable program that reads sampled A/D converted data from serial port and writes to the serial port for D/A conversion.
In the polling-based method, the program waits until the codec finishes the A/D conversion and reads the data when it’s ready. The program continuously checks the appropriate bits of the SPCR register to monitor the availability of the data.
Write an assembly program that reads the A/D conversion data and directly writes back the same data for D/A conversion. By connecting an audio source and a powered speakers to the DSK board, you should be able to hear the input sound. Depending on the input signal level, it may be necessary to multiply a constant around 4 to 8 to the A/D data before D/A conversion to amplify the signal.
Using interrupts, you don’t have to continuously check the data availability. By servicing the RINT1 interrupt, you can read the A/D data every time they become available. While waiting for the interrupt, the program can execute other instructions. In our case, the program simply idles waiting for interrupts.
Write an assembly program that reads the A/D conversion data and directly writes back the same data for D/A conversion. Using the XINT1 (or RINT1) interrupt, the A/D result is read and written back for D/A conversion in the interrupt service routine. The program simply idles while waiting for interrupts. Because both A/D and D/A conversions occur at 48kHz rate, you need to service only one interrupt (use XINT1 here) to sample the signal.
In the previous experiments, you were able to sample the input signal and listen to it through the D/A converter. Sometimes, it is often necessary to display the sampled data and analyze them using MATLAB-like software. The following experiment demonstrate how you can view and save memory contents using Code Composer.
You can define a buffer by allocating a memory space in your assembly program:
1 buffer .space 512
Define two 512 bytes buffers, one for each channel. Name the buffers buffer_left and buffer_right. Then, add extra lines of code so that each time the CPU reads from codec, the 32 data value is split into 2 halfwords corresponding to the left and right channels. Save each channel data in the buffers starting at the top. When the buffer is full, you start overwriting the buffers from the top of the buffer again. Build the code and run. Input 10kHz sine wave to the audio codec input using the sound card tone generator. While the input is still connected, halt the CPU. The sample values remain stored in memory location where the buffer_left and buffer_right is assigned.
You can view the content of buffer memory using VIEW:MEMORY on CCS. Enter the following values to the memory view window shown in Figure 1.
Plot the buffer content using VIEW:GRAPHT:TIME/FREQUENCY on CCS. Enter the values to the graph view window as shown in Figure 2.
You can also plot the spectrum of the signal in the buffer using VIEW:GRAPHT:TIME/FREQUENCY on CCS. Change the Display Type to FFT Magnitude. The CCS will display the magnitude of the FFT of the specified data.
Save the contents of buffer as a hex file using FILE:DATA:SAVE. After entering the file name, enter the address and length of buffer as buffer_left (or buffer_right) and 512. Using the MATLAB function load_vector, read the saved hex file into MATLAB. (You can download load vector.m MATLAB script from the course web page.) Plot the signal and its spectrum and compare them with what you obtained on CCS.
Repeat the above experiment using your voice as the input source. For input, you can record a piece of your voice using the Windows voice recorder and play it through the soundcard. The soundcard output is fed to the codec input. Plot the spectrum of your voice. In which frequency range is the energy of your voice concentrated?
| Memory View Window |
|---|
![]() |
| Graph Property Dialog Box |
|---|
![]() |