I am fairly new to programming microcontrollers. I have some experience with the Arduino, but after I had nearly finished my project, I deceided to moved my current project to something cheaper and smaller. So I am now using the AVR ATmega32 with Atmel studio.
I'm trying to use the ATmega32 to communicate to a MAX7219 chip for multiplexing with an led matrix. however, I do have multiple different devices that I want to communicate with.
How can I communicate with the device without actually using the SPI pins provided on the microcontroller? I have made a test project, but there seems to be something wrong and I can't figure it out what the issue is. I think I managed to get it in test mode, because all LEDs are lit, but I can't get it to do anything after that. I can't even clear the display/shut it down. I have checked the wiring over again. Is my coding perhaps incorrect as far as pin configuration and assigning the pins? Are there any suggestions for this or a better way to write my code?
Here is the link to the MA7219 datasheet
//test
#include <avr/io.h>
int main(void)
{
DDRB = 0b00000111; // pin 1(data), 2(clock) and 3(latch) are outputs
PORTB = 0 << PINB0; // data pin 1 is low
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low
uint16_t data;
data = 0b0000110000000000; // data to shift out to the max7219
//read bit
uint16_t mask;
for (mask = 0b0000000000000001; mask>0; mask <<= 1)
{
//iterate through bit mask
if (data & mask)
{ // if bitwise AND resolves to true
// send one
PORTB = 1 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}
else{ //if bitwise and resolves to false
// send 0
// send one
PORTB = 0 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}
}
PORTB = 1 << PINB2; // latch all the data
PORTB = 1 << PINB0; // data pin 1 is high
PORTB = 0 << PINB1; // clock pin 2 is low
PORTB = 0 << PINB2; // latch pin 3 is low
}
Yes, your bit-bang code has a problem in that you assign the entire value of the register each time without preserving the existing value. Hence you erase your data signal at the instant you drive your clock, violating the hold time of the receiver and resulting in unpredictable operation.
Instead of assigning pins with =, you should set them with |= or clear them with &= ~(value)
For example:
PORTB = 1 << PINB0; //drive data
// tick
PORTB |= 1 << PINB1; //clock high PRESERVING data
// tock
PORTB &= ~(1 << PINB1); //clock low
You may also need to insert a slight delay between pin operations.
Technically, given that you are already using an if for the data state, you could also re-drive the data signal with an OR in the assignment, for example
if (data & mask)
{ // if bitwise AND resolves to true
// send one
PORTB = 1 << PINB0;
// tick
PORTB = (1 << PINB1) | (1 << PINB0);
// tock
PORTB = 0 << PINB1 | (1 << PINB0);
}
else{ //if bitwise and resolves to false
// send 0
// send one
PORTB = 0 << PINB0;
// tick
PORTB = 1 << PINB1;
// tock
PORTB = 0 << PINB1;
}
Related
I cannot understand why my code won't work. It works when I set the condition PORTA == 0x00 but not when PORTA == 0x01. How do you check if a bit is high? Below is my code and my schematic (Crystal frequency is 4MHz).
#include<xc.h>
void main(){
int cnt;
int delay_cnt;
TRISA = 1; // PortA as input
TRISB = 0; // PortB as output
PORTB = 0x00; // Initialize LED as off
for(;;){ // Infinite loop
if(PORTA == 0x01){
for(cnt=0;cnt<3;cnt++){
PORTB = 0x01; // Turns on LED
for(delay_cnt=0;delay_cnt<10000;delay_cnt++);
PORTB = 0x00; // Turns off LED
for(delay_cnt=0;delay_cnt<10000;delay_cnt++);
}
}
}
}
In this case if (PORTA == 0x01), you are checking the whole port (8 bit).
If you want to check just bit 0 from the port use this:
if (PORTAbits.RA0 == 1){
or
if (PORTA & 0x01){
I'm trying to write a code in Atmel so I can control atmega32 PORTB's pins via switches connected
to PINA, it works but there is only one problem, if one switch is left high and an another switch
turns high the led connected(relates) to the second pin doesn't change, where did i go wrong?
#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 1000000UL //Setting the CPU frequency to 1MHz
toggle(void){ //Creating a toggle function to call it within the main function
unsigned char input = 0x00; //Creating an 8-bit variable so we can save the input port
int t = 1; //Loop variable
while(PINA != 0x00){
t = t + 1;
_delay_ms(1);
if(t == 100) break;
}
while(t != 100){
if((PINA != 0x00) && (input !=PINA))
{
input = PINA;
PORTB = PORTB^input;
t = t + 1;
_delay_ms(1);
}
else if(input == PINA)
{
t = t + 1;
_delay_ms(1);
}
else if(PINA == 0x00){
t = t + 1;
_delay_ms(1);
input = PINA;
}
}
}
int main(void)
{
DDRA = 0x00; //Setting PORTA as input
DDRB = 0xFF; //Setting PORTB as output
DDRC = 0xFF; //Setting PORTC as output
DDRD = 0x02; //Setting PORTD.2 as output
PORTB= 0x00; //Setting PORTB's output to zero
PORTC= 0x00; //Setting PORTC's output to zero
PORTD= 0x02; //Setting PORTD.2's output to one
while (1)
{
PORTD= 0x02; //Turning on the PORTD LED
toggle(); //Toggling the PORTB's LEDs
PORTD= 0x00; //Turning off the PORTD LED
toggle(); //Toggling the PORTB's LEDs
}
}
Analyze your code
look at this section
while(PINA != 0x00){
t = t + 1;
_delay_ms(1);
if(t == 100) break;
}
while(t != 100){
//your algorithm body processed here
}
if one of switches (which connected to PORTA) is left high then the first while loop condition PINA != 0x00 is always true the loop finish only when t==100, then, when you go to the second while Loop it will not excite because the condition t != 100 is already false
in other words your toggle() function will not process any data until you release all switches
solution
I have made a simple code for what you try to do
int main(void)
{
DDRA = 0x00; //Setting PORTA as input
DDRB = 0xFF; //Setting PORTB as output
DDRC = 0xFF; //Setting PORTC as output
DDRD = 0x02; //Setting PORTD.2 as output
PORTB= 0x00; //Setting PORTB's output to zero
PORTC= 0x00; //Setting PORTC's output to zero
PORTD= 0x02; //Setting PORTD.2's output to one
unsigned char currentInput=PINA;
unsigned char previousInput=PINA;
unsigned char changedBitsMusk = 0;
while (1)
{
currentInput = PINA; //update current reading
changedBitsMusk = currentInput ^ previousInput; // calculate what is changed from previous reading
if (changedBitsMusk) // if there is change happen
{
if (currentInput&changedBitsMusk) // only process the data if change is HIgh (i.e only when button is pressed)
{
PORTB^=changedBitsMusk; // toggle the corresponding pins
}
}
previousInput = currentInput;
}
}
When you are trying to control outputs according to another port inputs, you can simply do something like
PORTB = PINA;
Of course, if you don't need the full port you can always mask the bits you need from the entire port.
Also, this is not the way to do it if you don't have the same pin numbers paired in both ports (i.e.: if the pin 3 of one port controls the behavior of the pint 6 of the other, for example)
This way you can avoid a lot of ugly and complex logic
If you want to continue in the path you are going, try removing the "else" from the "else if"s, they are not needed and as far as I can see, the conditions are mutually exclusive
I'm trying the learn timers and pwm methods. I use the timer2 for fast pwm but i just want to use only pd3 for pwm. The code below works two pins together as pwm. How can i split these pins.
mcu is Atmega328p
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile unsigned char duty_cyc_a,duty_cyc_b;
int main(void)
{
DDRB = 0b00001000;
DDRD = 0b00001000;
PORTD = 0x00;
PORTB = 0x00;
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
TCNT2 = 0;
OCR2A = 117;
OCR2B = 117;
duty_cyc_a=0;
duty_cyc_b=255;
while (1)
{
PORTB = (1<<3);
_delay_ms(1000);
PORTB &= 0;
_delay_ms(1000);
}
}
You should look at the manual, specifically the pages at the end of the section on timer2 to see how setting the registers controls the waveform and pins. The pins are called OC2A and OC2B, which are also names for PB3 and PD3.
Another tip for readable code is to use the macros that describe the pin functions, rather than combine them into one difficult binary number. If the previous writer had done that, your job would be easier.
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
should be
TCCR2A = 1 << COM2A1 | 1 << COM2B1 | 1 << WGM21 | 1 << WGM20;
TCCR2B = 1 << CS20;
Now you can see which bits the previous author set to 1. (The rest are set to 0).
Go through the tables in the register section to decide which pins you need to set in these two control registers instead. (You won't use COM2A1, and you will need to choose different WGMx pins.)
An external interrupt vector should occur when a 5v input is supplied to int0.
The interrupt should change a volatile integer flag to allow a LED to illuminate that is connected to a pin on PORTB. Compiles with no errors in Atmel studio. The problem is no change occurs when a 5v supply is sent to the int0 pin. Is this that the interrupt is not triggering?
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
volatile int pwm_flag=0;
int main(void)
{
DDRD &= ~(1 << DDD2); // Clear the PD2 pin
// PD2 (PCINT0 pin) is now an input
PORTD |= (1 << PORTD2); // turn On the Pull-up
// PD2 is now an input with pull-up enabled
EICRA |= (1 << ISC00)|(1 << ISC10); // set INT0 to trigger on Rising edge
EIMSK |= (1 << INT0); // Turns on INT0
sei(); // turn on interrupts
DDRB = 0xFF;
PORTB = 0x00;
while(1)
{
if(pwm_flag==1)//if flag is raised
{
PORTB = 0xFF;//turn on all pins of portb
pwm_flag=0;//reset flag to 0
}
}
}
ISR (INT0_vect)
{
/* interrupt code here */
pwm_flag =1;//raise flag
}
The problem is no change occurs when a 5v supply is sent to the int0 pin.
How do you send 5V to the INT0 pin? Your code is setting INT0 pin as an input with pullup so unless you short it to the ground it is at 5V.
Also, what do you mean by no change occurs? Is the LED on or off?
Another thing is that the line:
EICRA |= (1 << ISC00)|(1 << ISC10);
sets both INT0 and INT1 to occur on any logical change on appropriate pin. Bits with name ISC0x control INT0, bits called ISC1x are used to configure INT1. In your code you are mixing both and you end up with the following configuration:
ISC01 | ISC00 | meaning
-------------------------------------------------------------------------
0 | 1 | any logical change on INT0 generates an interrupt request
ISC11 | ISC10 | meaning
-------------------------------------------------------------------------
0 | 1 | any logical change on INT1 generates an interrupt request
If you are trying to supply 5v from a pin on PORTB to an LED then perhaps that is your problem. Most micro-controller pins can sink more current than they can give. Perhaps your LED is just not getting the current it needs?
Your wiring should be as follows:
Connect the positive leg of the LED to 5V. Connect the negative leg to a small resistor 100-500 Ohms. Connect the other leg of the resistor to the pin on PORTB.
Now you can write PORTB to 0x00 to turn on the LED or 0xFF to turn it off.
To test that the LED is working test the LED in your main loop by writing PORTB low and high with a noticeable delay in between.
If that works. Then test your ISR. If the ISR doesn't work a this point then the ISR is the problem.
Bear in mind that in your current configuration of EICRA you are triggering an interrupt on a rising edge. So if the pin is already high not interrupt will occur.
I have changed your code below so LOW is ON and HIGH is OFF.
int main(void){
DDRD &= ~(1 << DDD2); // set PD2 DDR as input
PORTD |= (1 << PORTD2); // set PD2 as input pull-up
EICRA |= (1 << ISC00)|(1 << ISC10); // set INT0 to trigger on rising edge
EIMSK |= (1 << INT0); // Turns on INT0
DDRB = 0xFF; // set PORTB as all outputs
PORTB = 0xFF; // set PORTB high
sei(); // turn on interrupts
while(1){
if(pwm_flag!=0){ // check flag
PORTB = 0x00; // set PORTB low
pwm_flag=0; // reset flag
}
}
ISR (INT0_vect){
pwm_flag = 1; // raise flag
}
i am new to Atmel, so maybe the question is quite simple.
I have the following situation:
I have an ATMega2560 and want to get Interrupts on the Pins PK0-PK7. I am interested in the PIN Change from Low to HIGH. (I have connected one photocell to every PIN)
I have read that the interrupt for PCINT[0-2] are fired everytime (pin high and pin low) so i defined an array to ignore the PIN DOWN Interrupt.
So i have the following code:
Note: I do not understand, why i need to set DDRC as input and not DDRK ?
#define HIGH 1
#define LOW 0
volatile uint8_t portbhistory = 0xFF;
uint8_t pinState[8];
void initSystem()
{
int i;
for(i = 0; i < sizeof(pinState) / sizeof(*pinState); i++)
{
pinState[8] = 0;
}
DDRB = 0xff; // set Port B as output
DDRC = 0; // WHY DDRC ?? PCINT == PK0-7, so DDRK ? // all Pins input
PORTC = 0xff; // same question ... // turn on pullup for every pin
PCICR |= _BV(PCIE2); // enable interrupt for PCIE2
PCMSK2 = 0xff; // Interrupt at all pins
sei(); // turn on interrupts
}
int main(void)
{
initSystem();
while(1)
{
}
}
ISR(PCINT2_vect)
{
int i;
uint8_t changedbits;
changedbits = PINC ^ portbhistory;
portbhistory = PINC;
/*
if(pinState[changedbits] == HIGH)
{
pinState[changedbits] = LOW;
return;
} else
{
pinState[changedbits] = HIGH;
}
*/
setDebugLED(1);
_delay_ms(30);
setDebugLED(0);
}
If i connect 5V to one of the PINS (PK0-7) the LED flashes instantly. But if i disconnect the 5V it takes ~2seconds for the LED to flash again. In this time the one pin i connected 5V before does not let the LED flash again if i connect the 5 V again.
Other Ports work in this time.
So you could say, that the PIN HIGH interrupt for all pins work fine and get fired instantly but the PIN LOW interrupts need some time (~2sec.) In this time the port is "disabled" or somethig.
Can anyone help me with that?
EDIT
Just some wrong edit ...
EDIT 2
So, i totally forgot to post my solution here. sorry for that!
The solution is: My external circuit pulls the PIN to Ground or to 5V.
Thank you!