I'm building a drum machine, and I have stored a sample header file with a kick sound which takes values between 0 and 170. I want to send it over SPI to a 10-bit MCP4811 DAC which then outputs it to a 3.5mm audio jack.
I have my MISO,MOSI,SCK and RESET pins connected to my USB programmer as well as the DAC.
Here is a snippet of the audio file stored in "samples.h"
unsigned const char sample_1[2221] PROGMEM = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, ...}
unsigned int sample_len[1] = {2221}
So it is a sample of 2221 bits. This I want to send to the DAC using SPI with freq = 22 kHz.
I'm using a 16 MHz Crystal, so I set the fuses accordingly to use it.
I am using a Timer which overflows 22 kHz.
volatile unsigned int sample_count[1] = {0};
volatile unsigned int audio_out = 0;
volatile unsigned char spi_junk;
int main (void)
sei();
DDRB = 0b10110000; //Set MOSI, SCK and SS as output.
PORTB = (1 << PINB4) //active low on SS.
TIMSK1 = (1<<OCIE1A); //Enable interrupt
TCCR1B = (1<<WGM12) | (1<<CS11); // set CTC mode and divide clk by 8
OCR1A = 91; //16 MHz/(8*91) ~ 22068 Hz
//SPI Init
SPCR = (1<<SPE) | (1<<MSTR); //master, 8 MHz
SPSR = (1<<SPI2X);
ISR (TIMER1_COMPA_vect) {
audio_out = 0;
//If play_track == 1, then the sound should be played back.
if (play_track && sample_count[0] < sample_len[0]){
audio_out += (pgm_read_byte(&(sample_1[sample_count[0]++)));
// send audio_out to 10-bit DAC on SPI
PORTB &= ~(1<<PINB4); // B.4 (DAC /CS)
SPDR = (char) ((audio_out >> 6) & 0x000f); //byte 1 0 0 0 0 b9 b8 b7 b6
while (!(SPSR & (1<<SPIF)));
spi_junk = SPDR;
SPDR = (char) ((audio_out & 0x003f) << 2); //byte 2 b5 b4 b3 b2 b1 b0 0 0
while (!(SPSR & (1<<SPIF)));
spi_junk = SPDR;
PORTB |= (1<<PINB4);
}
My PIN set up is.
Atmega644 -> DAC
MOSI -> SDI
SCK -> SCK
SS -> /CS
On MCP4811
Vdd -> 5V
Vss -> GND
V_out -> Audio jack.
The rest of the pins on the MCP4811 are not connected to anything.
I have seen that the audio_out is working as expected, by displaying the audio_out value on a LCD screen. But nothing is being output to the DAC. Anyone see what could be wrong?
EDIT: Added the SPI init which I had missed to add.
Your line here
SPDR = (char) ((audio_out >> 6) & 0x000f); //byte 1 0 0 0 0 b9 b8 b7 b6
Will set ¬SHDN to 0 which will shutdown the DAC
0 = Shutdown the device. Analog output is not available. VOUT pin is connected to 500 kohm typical)
Set bit 12 to 1 instead
SPDR = (char) ((audio_out >> 6) & 0x0f)|0x10; //byte 1 0 0 0 1 b9 b8 b7 b6
from datasheet
1 = Active mode operation. VOUT is available.
There is no SPI initialization in your code.
add into main()
SPSR = (1 << SPI2X); // double speed (to get maximum of 8MHz output)
SPCR = (1 << SPE) | (1 << MSTR); // 1:1 prescaler, master mode, SPI mode 0, SPI enable
Also several remarks to your code:
use sei() only after all initializations are done, to avoid interrupts happened on uninitialized peripheral.
better first set PB4 high then setup it as output to avoid PB output low between two commands:
PORTB = (1 << PINB4) //active low on SS.
DDRB = 0b10110000; //Set MOSI, SCK and SS as output.
Related
I'm trying to read two ADC channels sequentially from my STM32F407ZGT6 using DMA. I'm just trying to get the values from two potentiometers independently on each channel. Although the program doesn't crash, I does not update my variable's value (sensor_val).
I'm using DMA2_Stream0 Channel 0, since I'm using ADC1. For my ADC1, I'm using PB1 (channel 9) and PA1 (channel 1). I tried to follow this tutorial, except that I do not want to trigger my ADC from a timmer just yet, and I've been also checking the example on this question. My ADC callback also never gets called. As far as my understanding goes, the sequence of calls should be:
ADC1->SR EOC --> ADC->CR1 EOCIE --> DMA2_Stream0_IRQHandler() --> dma_ADC_callback()┐
⮤─────────────────────────────────────────────────────────┘
Maybe I do need to include a periodic call to read the ADC?
All the ADC/DMA functions are on their separate .c/.h files. I've already tried to declare sensor_val as a global variable or as an extern variable from the adc.c file, and both give the same result. Here is an approximate mwe of my code:
#include <stdint.h> //uint32_t
#include <stdio.h> //printf
#include "stm32f407xx.h"
#define set(val, pos) ((val) << (pos))
#define msk(size, pos) (((1UL << (size)) - 1UL) << (pos))
static uint32_t sensor_val[2];
static void dma_ADC_callback(void);
void gpio_init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Port A
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Port B
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Port C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //Port D
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Port F
RCC->AHB1ENR;
//PORT B
GPIOA->MODER |= set(m,(2)); //ADC || PA1 || ADC123_IN1
GPIOB->MODER |= set(m,(2)); //ADC || PB1 || ADC12_IN9
}
void adc_init(void)
{
/*Enable clock access to ADC*/
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
/* Config ADC parameters*/
/* Regular Sequence Register 3
* Since the sequence starts from
* the back, we need to set channels
* from SQ3[0] to SQ1[19]
*/
ADC1->SQR3 |= set(0b1001,0); //sets channel PB1 (ADC12_IN9) as 1st conversion
ADC1->SQR3 |= set(0b0001,5); //sets channel PA1 (ADC123_IN1) as 2nd conversion
ADC1->SQR1 |= set(0b0001,ADC_SQR1_L_Pos); //tells the channel sequence lenght = 2
/*If using more than one channel
* SCAN is required
*/
ADC1->CR1 |= set(1,ADC_CR1_SCAN_Pos);
/*Adjust ADC sample time
* The resulting frequency is
* APB2/#cycles:
* 000: 3 cycles
* 001: 15 cycles
* 010: 28 cycles
* 011: 56 cycles
* 100: 84 cycles
* 101: 112 cycles
* 110: 144 cycles
* 111: 480 cycles = 42MHz/480 = 87.5kHz
* */
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP0_Pos); //channel 0
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP9_Pos); //channel 9
/*Turn Interruption On*/
ADC1->CR1 |= set(1,ADC_CR1_EOCIE_Pos);
/*Enable ADC*/
ADC1->CR2 |= set(1,ADC_CR2_ADON_Pos);
}
void adc_start_conversion(void)
{
ADC1->CR2 |= set(1,ADC_CR2_EOCS_Pos); //enables multi-channel conversion
ADC1->CR2 |= set(1,ADC_CR2_CONT_Pos); //enables continuous conversion
ADC1->CR2 |= set(1,ADC_CR2_SWSTART_Pos); //starts conversion
}
/*ADC1 DMA2 => DMA2_Ch0_Stream0 and 4*/
/*ADC2 DMA2 => DMA2_Ch1_Stream2 and 3*/
/*ADC3 DMA2 => DMA2_Ch2_Stream0 and 1*/
void dma2_stream0_init(uint32_t memo, uint32_t periph, uint32_t len)
{
/*Enable clock acces to DMA*/
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
/*Diable DMA2 Stream 0*/
DMA2_Stream0->CR &= ~DMA_SxCR_EN;
/*Clear all interrupt flags of Stream 0*/
DMA1->LIFCR |= DMA_LIFCR_CFEIF0;
DMA1->LIFCR |= DMA_LIFCR_CDMEIF0;
DMA1->LIFCR |= DMA_LIFCR_CTEIF0;
DMA1->LIFCR |= DMA_LIFCR_CHTIF0;
/*Set the source buffer*/ //Memory Address
DMA2_Stream0->M0AR = memo;
/*Set destination buffer*/ //Peripherial Address
DMA2_Stream0->PAR = periph;
/*Set the length*/
DMA2_Stream0->NDTR = len;
/*Set Control options
* Select Stream0_CH0 |
* Prioritu Lvl = High |
* Memory Increment On |
* Circular mode on |
* Direction Per->Mem (0b00)|
* Enable Transfer Complete interrupt
*/
DMA2_Stream0->CR &= ~(DMA_SxCR_CHSEL |
DMA_SxCR_PL |
DMA_SxCR_MSIZE |
DMA_SxCR_PSIZE |
DMA_SxCR_PINC);
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos)
);
/*Enable direct mode and disable FIFO*/
DMA2_Stream0->FCR = 0;//set(0,DMA_SxFCR_FEIE_Pos);
/*!! Enable DMA1 Stream 6*/
DMA2_Stream0->CR |= set(1,DMA_SxCR_EN_Pos);
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
/*DMA Interrupt enable in NVIC*/
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
void DMA2_Stream0_IRQHandler(void)
{
/*Check for transfer complete interrupt*/
if(DMA2->LISR & msk(1,DMA_LISR_TCIF0_Pos))
{
//Clear flag
DMA2->LIFCR |= msk(1,DMA_LIFCR_CTCIF0_Pos);
//Callback
dma_ADC_callback();
}
}
static void dma_ADC_callback(void)
{
printf("[ Readings Pots.: | %li | %li ]\r\n", sensor_val[0], sensor_val[1]);
}
int main(void)
{
/*Setup*/
//clock_init_168(); //SYSCLK = 168MHz, AHB = 84MHz, APB1 = 42MHz, APB2 = 84MHz
//init_systick_MS(SYSTICK_LOAD_VAL_MS);
gpio_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_init();
adc_start_conversion(); //continuous conversion
char count = 0;
for(;;)
{
debug_msg("count : %d", __PRETTY_FUNCTION__, count);
delayMS(500);
count++;
}
}
I finally found a way to fix it!
First of all, the ADC has to be enabled before the DMA. With the code above, ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos) wouldn't get set because I was trying to set the DMA before the ADC. Hence, my interruption never got called. The correct order of function calls is:
adc_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_start_conversion();
However, after ADC_CR2_DMA was getting set, my callback got called, but only once. So I had to disable DMA selection with ACD1->CR2 = ADC_CR2_DDS, so DMA would issue conversion requests recurrently:
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= (set(1,ADC_CR2_DMA_Pos) | //<<make sure ADC is already enabled
set(1,ADC_CR2_DDS_Pos)); //<<without DDS, the DMA does a single conversion
Once I did that, my callback got called and I was reading data, but only sensor_val[0] was being updated. That was happening because I had PSIZE == 00, which is the value used to set MSIZE in direct mode. Since I'm using DMA for ADC (maximum 12-bit resolution), I changed sensor_val to uint16_t and PSIZE = 0b01:
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_PSIZE_Pos) | //<<sets MSIZE=PSIZE in direct mode
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos));
And voilá!
NOTES
PINC should be kept at reset value for this configuration, otherwise ADC will expect data with more than 16-bits.
If you need slower rates of acquisition, instead of setting DDS, a DMA request function can be created where the DMA bit is reset and set again:
void adc_new_dma_conversion(void)
{
/*DMA has to be reset first
* then re-enabled to generate
* a new DMA request
*/
ADC1->CR2 &= ~set(1,ADC_CR2_DMA_Pos);
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
}
I am currently working with an STM32F407VG on the Discovery board. I am going through the peripherals and trying to get each one working by manipulating the registers only (no HAL).
When I go to initialize UART2 it is transmitting the character I write to the DR but it is doing so ~12x faster than expected, I'm shooting for 9600 baud. I measured this using an oscilloscope (~8-9 us per bit) and playing with the baud rate in Putty (111,111 baud to show the actual character).
I am maxing out the clock speed of the chip 168 MHz SYSCLK, APB1 Prescaler "/4", APB2 Prescaler "/2". I am pretty sure my clocks are at what they are supposed to be, to verify I set up TIMER12, which shares the APB1 clock and set the prescaler to 8400 and had an interrupt generated every time there was a compare match (CCR = 5000) and overflow. I measured this with the oscilloscope and I am getting a 1 Hz square wave as expected which means that the APB1 for Timer 12 is at 84 MHz.
Here is my clock init code:
void SysClockConfig ( void ){
//setting up the MCO output to see the clock signal
//RCC->CFGR |= ( 6 << 24 ) | (3 << 21 ) | ( 4 << 27 );
//1. ENABLE HSE and wait for the HSE to become Ready
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
// 2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// 3. Configure the FLASH PREFETCH and the LATENCY Related Settings
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_5WS;
//4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
// AHB PR
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// APB1 PR
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
// APB2 PR
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
//5. Configure the MAIN PLL
RCC->PLLCFGR = (PLL_M << RCC_PLLCFGR_PLLM_Pos) | (PLL_N << RCC_PLLCFGR_PLLN_Pos) | (PLL_Q << RCC_PLLCFGR_PLLQ_Pos) | (RCC_PLLCFGR_PLLSRC_HSE);
//6. Enable the PLL and wait for it to become ready
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
//7. Set source
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
This is my UART2 config code:
void UART2_Config ( void )
{
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //clock UART
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //clock GPIOA
GPIOA->MODER |= ( 2 << GPIO_MODER_MODER2_Pos ) | ( 2 << GPIO_MODER_MODER3_Pos ); //set PA2 and PA3 alternate function
GPIOA->OSPEEDR |= ( 3 << GPIO_OSPEEDR_OSPEED2_Pos ) | ( 3 << GPIO_OSPEEDR_OSPEED3_Pos ); //clock GPIO pin at fastest speed
GPIOA->AFR[0] |= ( 7 << GPIO_AFRL_AFSEL2_Pos ) | ( 7 << GPIO_AFRL_AFSEL3_Pos ); //set PA2 and PA3 to alt func UART2
USART2->CR1 = 0;
USART2->CR1 |= USART_CR1_UE; //UART Enable
//USART2->BRR = (0x16 << USART_BRR_DIV_Mantissa_Pos) | (0xc << USART_BRR_DIV_Fraction_Pos); //Set Baud rate
USART2->BRR = 4300;
//USART2->CR1 |= USART_CR1_RE; //Receiver enable
USART2->CR1 |= USART_CR1_TE; //Transmitter enable
//Baud rate is off by a factor of 12ish
}
Finally, my main and while loop with the function for sending a character:
void UART2_SendChar ( uint8_t c )
{
USART2->DR = c;
while ( !(USART_SR_TC));
}
int main ( void )
{
SysClockConfig();
GPIO_Config();
TIM10_Config();
TIM12_Config();
UART2_Config();
//NVIC_SetPriority (TIM1_UP_TIM10_IRQn, 1);
//NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
NVIC_SetPriority (TIM8_BRK_TIM12_IRQn, 1);
NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
while ( 1 )
{
//GPIOD->BSRR |= (1<<12);
//delay(2000000);
//GPIOD->BSRR |= (1<<28);
delay(4000000);
UART2_SendChar('a');
}
}
I can't find anything in the reference manual to explain this behavior. That tells me I am doing something wrong but I can't seem to track it down. On a final note, I had Putty set up to receive 9600 baud and played around with the BRR and setting it to a value of 4300 output the desired character. Plugging that value into the equation for baud in the reference manual gave me an insane system clock 660 MHz, again telling me I am missing something probably pretty obvious.
I've answered a similar question here recently, so let me copy that one:
BRR is a Q12.4 format fixed point number, which needs one additional operation when OVER8 mode is selected.
OP's question is about STM32F4, but if one inspects STM32F030 reference manual, he/she can find the same logic but represented by a different formula. IMO, that one (F030) is more clear and easier to understand.
To convert uint16_t (Q16.0) to Q12.4, you need to multiply it by 16.
Basically, for OVER16 case, BRR is simply becomes PeripheralClock / BaudRate.
For OVER8 case, you first need to calculate a temporary variable as temp = 2 * PeripheralClock / BaudRate. When writing it into BRR, you need to right-shift its lower 4 bits once.
See the example code:
void setBaudRate(uint32_t baud, bool over8)
{
const uint32_t pClock = 42000000ull; // Hard-code or call a function to obtain it
uint32_t usartDiv = (over8) ? (2 * pClock / baud) : (pClock / baud);
uint32_t reg = USART1->BRR;
reg &= ~0xffff; // Clear the lower 16 bits
if (over8) {
reg |= (usartDiv & 0xfff0) | ((usartDiv & 0xf) >> 1);
}
else { // over16
reg |= usartDiv;
}
USART1->BRR = reg;
}
So, for your case, the correct value of BRR is 4375.
I'm using I2C converter to send data to my lcd.
The converter is based on PCF85741, and the lcd is Hitachi hd44780.
Port mapping between PCF85741 and lcd is as follows:
P0 -> RS
P1 -> RW
P2 -> E
P3 -> ?
P4 -> D4
P5 -> D5
P6 -> D6
P7 -> D7
The documentation says that the default address of my slave is 0x20, but with RW bit I need to send 0x40.
Here is my code:
void twi_start()
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA);
while (!(TWCR & (1 << TWINT)));
}
void twi_stop()
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while (!(TWCR & (1 << TWSTO)));
}
void twi_write(uint8_t byte)
{
TWDR = byte;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
}
void twi_write_byte(uint8_t byte)
{
uint8_t SLAVE_ADDRESS = 0x40;
twi_start();
twi_write(SLAVE_ADDRESS);
twi_write(byte);
twi_stop();
}
Lcd init
void lcd_init2()
{
for (int i = 0; i < 3; i++) {
twi_write_byte(0x03);
_delay_ms(20);
}
twi_write_byte(0x02);
_delay_ms(20);
//4 bit mode
twi_write_byte(0x24); // D5 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x20); // D5 -> 1, E -> 0
_delay_ms(10);
//2 lines
twi_write_byte(0x24); // D5 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x20); // D5 -> 1, E -> 0 first nibble
_delay_ms(10);
twi_write_byte(0x84); // D7 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x80); // D7 -> 1, E -> 0 second nibble
_delay_ms(10);
}
After this code, the lcd should be in 4bit mode, with 2 lines, but it isn't
Nothing changes on lcd.
1) Please clarify what the I2C-to-parallel IC you have? I cannot find what is PCF85741, I see datasheets only for PCF8574 and PCF8574A.
In the first case, the slave address will be (including r/w bit) 0x40...0x4F, in the second - 0x70...0x7F.
Speaking from my experience the usual I2C circuit which comes with those displays has PCF8574A on it (i.e. address is 0x7*). And pin 3 there, by the way, is used to control the backlight.
2) Make sure what lower bits of the address you have? Are inputs A0 A1 A2 pulled up or tied to ground?
Again, from my experience, those boards usually have pull-ups to +5, and solderable jumpers on the board, to short those to ground. By default, the jumpers are not soldered, therefore A0 A1 A2 has a high logical level, thus the I2C address of the device is 0x7E (writing)/0x7F (reading). If you have PCF8574T on board, then the address will be 0x4E/0x4F
You can easily detect has the IC answered or not, by checking TWS bits in TWSR register (TWSR & TW_STATUS_MASK) should be equal to TW_MT_SLA_ACK (0x18) after the address is transmitted, or TW_MT_DATA_ACK (0x28) after the data byte is transmitted. (See the datasheet section 19.8.1 Master Transmitter Mode pages 186-188)
Or, more simply, if you have P3 of the PCF8574 connected to the backlight, you can try output 0x08 / 0x00 to see if the backlight is turning on and off.
3) For the initialization sequence look at Figure 24 4-Bit Interface on page 46 of HD44780 datasheet
Note bits DB5 and DB4 are high. Also, be careful, since you're only writing to the display controller, the bit R/W (i.e. the bit 1 of the output) should always be zero (note you're sending wi_write_byte(0x03); and then twi_write_byte(0x02); with the bit 1 is set high).
The example may be as follows:
void send4bits(uint8_t fourbits, bool is_cmd) {
uint8_t d = (fourbits << 4) | 0b1000;
if (!is_cmd) d |= 1;
twi_write_byte(d | 0b100); // E high
twi_write_byte(d); // E low
}
void sendcmd(uint8_t cmd) {
send4bits(cmd >> 4, true);
send4bits(cmd & 0xF, true);
}
void senddata(uint8_t cmd) {
send4bits(cmd >> 4, false);
send4bits(cmd & 0xF, false);
}
// initialization sequence
send4bits(0b0011, true);
_delay_ms(5);
send4bits(0b0011, true);
_delay_ms(1);
send4bits(0b0011, true);
// since I2C is slow enough the required 100us pause already happened here
send4bits(0b0010, true);
sendcmd(0b00101000);
sendcmd(0b00001000);
sendcmd(0b00000001);
delay_ms(2);
sendcmd(0b00000110);
sendcmd(0b00001110);
// Initialization is done
sendcmd(0x80); // Set cursor at the beginning
for (uint8_t i = 'A' ; i <= 'Z' ; i++) {
senddata(i); // Send some random data
}
So I'm trying to connect the chip with an 8-bit data bus. All works good, but...
Arduino Uno uses D0 and D1 for serial TX/RX (USB connection with PC). When I try to open a serial connection (on serial.begin stage) ports D0 and D1 blocks and the chip stops working. This is a problem.
I think I should relocate pins D0 and D1 to another port. But the chip uses 8 pins (8bit chip). So I need to relocate the first pin (D0) and the second pin (D1) to port b. Maybe I can use B4 and B3?
But I don't know how to use PORTD[PD2, PD3, PD4, PD5, PD6, PD7] and PORTB[PB4, PB3] in one time like:
var portX = PORTD[PD2, PD3, PD4, PD5, PD6, PD7] + PORTB[PB4, PB3];
portX = data;
My code:
#include <avr/io.h> // For I/O and other AVR registers
#include <util/delay.h> // For timing
/* Pinmap (Arduino UNO compatible) */
#define YM_IC (5) // PC5 (= pin A5 for Arduino UNO)
#define YM_CS (4) // PC4 (= pin A4 for Arduino UNO)
#define YM_WR (3) // PC3 (= pin A3 for Arduino UNO)
#define YM_RD (2) // PC2 (= pin A2 for Arduino UNO)
#define YM_A0 (1) // PC1 (= pin A1 for Arduino UNO)
#define YM_A1 (0) // PC0 (= pin A0 for Arduino UNO)
#define YM_CTRL_DDR DDRC
#define YM_CTRL_PORT PORTC
#define YM_DATA_DDR DDRD
#define YD0 (0)(= pin D0 for Arduino UNO)
#define YD1 (1)(= pin D1 for Arduino UNO)
#define YD2 (2)(= pin D2 for Arduino UNO)
#define YD3 (3)(= pin D3 for Arduino UNO)
#define YD4 (4)(= pin D4 for Arduino UNO)
#define YD5 (5)(= pin D5 for Arduino UNO)
#define YD6 (6)(= pin D6 for Arduino UNO)
#define YD7 (7)(= pin D7 for Arduino UNO)
#define YM_DATA_PORT PORTD // Whole PORT D for data bus (= pins D0 to D7 for Arduino UNO)
#define YM_MCLOCK (1) // PB1 = OC1A (= pin D9 for Arduino UNO)
#define YM_MCLOCK_DDR DDRB
extern uint8_t chflash[6];
uint8_t dacflash;
#define NOTEONFLASH 4
#define VGMWAIT 15
static void write_ym(uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_CS); // CS LOW
YM_DATA_PORT = data;
_delay_us(1);
YM_CTRL_PORT &= ~_BV(YM_WR); // Write data
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_WR);
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_CS); // CS HIGH
}
/**
* Write data into a specific register of the YM2612
*
* #author Furrtek
* #param reg Destination register address
* #param data Data to write
*/
static void setreg(uint8_t reg, uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_A0); // A0 low (select register)
write_ym(reg);
YM_CTRL_PORT |= _BV(YM_A0); // A0 high (write register)
write_ym(data);
}
void call() {
setreg(0x28, 0xF0); // Key on
_delay_ms(1000);
setreg(0x28, 0x00); // Key off
}
uint8_t getfilebyte() {
return Serial.read();
}
int main() {
init();
//Serial.begin(9600);
/* Pins setup */
YM_CTRL_DDR |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD) | _BV(YM_A0) | _BV(YM_A1);
YM_DATA_DDR |= _BV(YD0) | _BV(YD1) | _BV(YD2) | _BV(YD3) | _BV(YD4) | _BV(YD5) | _BV(YD6) | _BV(YD7);
YM_MCLOCK_DDR |= _BV(YM_MCLOCK);
YM_CTRL_PORT |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD); /* IC, CS, WR and RD HIGH by default */
YM_CTRL_PORT &= ~(_BV(YM_A0) | _BV(YM_A1)); /* A0 and A1 LOW by default */
/* F_CPU / 2 clock generation */
TCCR1A = _BV(COM1A0); /* Toggle OCA1 on compare match */
TCCR1B = _BV(WGM12) | _BV(CS10); /* CTC mode with prescaler /1 */
TCCR1C = 0; /* Flag reset */
TCNT1 = 0; /* Counter reset */
OCR1A = 0; /* Divide base clock by two */
/* Reset YM2612 */
YM_CTRL_PORT &= ~_BV(YM_IC);
_delay_ms(10);
YM_CTRL_PORT |= _BV(YM_IC);
_delay_ms(10);
/* YM2612 Test code */
setreg(0x22, 0x00); // LFO off
setreg(0x27, 0x00); // Note off (channel 0)
setreg(0x28, 0x01); // Note off (channel 1)
setreg(0x28, 0x02); // Note off (channel 2)
setreg(0x28, 0x04); // Note off (channel 3)
setreg(0x28, 0x05); // Note off (channel 4)
setreg(0x28, 0x06); // Note off (channel 5)
setreg(0x2B, 0x00); // DAC off
setreg(0x30, 0x71); //
setreg(0x34, 0x0D); //
setreg(0x38, 0x33); //
setreg(0x3C, 0x01); // DT1/MUL
setreg(0x40, 0x23); //
setreg(0x44, 0x2D); //
setreg(0x48, 0x26); //
setreg(0x4C, 0x00); // Total level
setreg(0x50, 0x5F); //
setreg(0x54, 0x99); //
setreg(0x58, 0x5F); //
setreg(0x5C, 0x94); // RS/AR
setreg(0x60, 0x05); //
setreg(0x64, 0x05); //
setreg(0x68, 0x05); //
setreg(0x6C, 0x07); // AM/D1R
setreg(0x70, 0x02); //
setreg(0x74, 0x02); //
setreg(0x78, 0x02); //
setreg(0x7C, 0x02); // D2R
setreg(0x80, 0x11); //
setreg(0x84, 0x11); //
setreg(0x88, 0x11); //
setreg(0x8C, 0xA6); // D1L/RR
setreg(0x90, 0x00); //
setreg(0x94, 0x00); //
setreg(0x98, 0x00); //
setreg(0x9C, 0x00); // Proprietary
setreg(0xB0, 0x32); // Feedback/algorithm
setreg(0xB4, 0xC0); // Both speakers on
setreg(0x28, 0x00); // Key off
setreg(0xA4, 0x22); //
setreg(0xA0, 0x69); // Set frequency
call();
while(1000) {
if (Serial.available() > 0) {
_delay_ms(1000);
call();
_delay_ms(1000);
char var = getfilebyte();
Serial.print("Yeah: ");
Serial.println(var);
} else {
Serial.print("Nooo: ");
Serial.println(Serial.available());
call();
}
}
while(1);
}
You need bitmath to put them together. Bitshift and bitwise or and bitwiase and. Look those up in the reference. What you want will look something like:
byte portX = (PORTD & 0xFC) | ((PORTB >> 3) & 0x03);
What you actually end up with might be slightly different though since I am making assumptions about what order you want the bits in. The first part masks off the high 6 bits of PORTD, the second part shift the two bits you want out of PORTB into the lowest two bits and masks them off. The | in the middle puts the two together.
A seceond answer because I think you're actually going the other way now that I re-read your question. I think you want to output to those pins. You'll need bitmath to do that as well.
Something like:
PORTB |= (data & 0x03) << 3;
PORTD |= data & 0xFC;
"There is no way to write to two different registers in the same instruction, so if you need all eight bits to change exactly at the same time you're out of luck." Answer from electronics. Maybe i can test my luck in multiple serial begin and end..
this is my code
#include "LPC17xx.h" // Device header
#include "GPIO_LPC17xx.h" // Keil::Device:GPIO
uint32_t voltag1 = 0 ;
uint32_t voltag2 = 0 ;
volatile uint32_t adstat;
int blink=1;
int main()
{
//Config timer
LPC_TIM1->MCR=2;
LPC_TIM1->MR0=20000000; //Match Resgister
LPC_TIM1->TCR=1;
LPC_TIM1->EMR = 0x00000030 ;
//Config ADC
LPC_PINCON->PINSEL1 |= (1 << 14) | (1 << 16); // connect pin to ADC
LPC_SC->PCONP |= ((1 << 12)); //enable power of ADC
LPC_ADC->ADCR = 0x06202001; //initialaze ADC
LPC_ADC->ADINTEN = 0x00000100; // global interup
NVIC_EnableIRQ(ADC_IRQn);
GPIO_SetDir(3,25,GPIO_DIR_OUTPUT);
while(1) {}
}
void ADC_IRQHandler(void)
{
adstat = LPC_ADC->ADSTAT; /* Read ADC clears interrupt */
blink++;
GPIO_PinWrite(3,25,blink%2);
voltag1 = (LPC_ADC->ADGDR >> 4) & 0xFFF;
//voltag2 = (LPC_ADC->ADDR1 >> 4) & 0xFFF;
}
when i use LPC_ADC->ADGDR it work fine but when i use LPC_ADC->ADDR1 its not working , why?
i used MAT for ADC interup
and when i use LPC_ADC->ADGDR everything works fine
but when i use LPC_ADC->ADDR1 for reading its not working and not change with MAT edge
There are way too many magic numbers in your code. Please use the corresponding defines for all those bits. This code is unreadable as is.
There is a wrong comment in your code, the interrupt flag for ADGDR is not cleared on ADSTAT read. Only a read of ADGDR clears it, and thats why your code does not work with ADDR1.
Had you used the definition to set ADINTEN, you might have spotted your error sooner. That flag is named ADGDINTEN and corresponds to the flag in ADGDR only.
You should enable IRQ of ch1 by:
LPC_ADC->ADINTEN = 1 << 1; //0x0002