How to convert 8Bit to decimal bar from 0 to 15? - c

Okay, here's what I'm trying to program in C.
I have an 8bit binary signal from an ADC on an ATmega32.
Now I want to convert that signal into an bar with 15LED's that increases the higher the input value is.
So basically I want to cut my 8bit signal down to a 4bit one, convert it to decimal and show it at an increasing bar.
I first had the idea to check if my input is in a specific range (which would be always a range of 255/15) but I just couldn't figure out how.
Just checking if the input is higher than a particular value won't work because in that way there could be more than one condition at a time true.
Have you any idea how I could solve that? Any help is highly appreciated. ;)
Thank you!

Use this:
uint8_t adc = GET_ADC_VALUE();
// Say LED is a 16-bit register
LED = (adc ? (1U << ((adc >> 4) + 1)) - 1 : 0x000);
So only ADC value 0 has all LEDs off, all other ADC values power on 1 to 16 LEDs. This has the advantage of not using any division (ATmega has no divisor instruction).
EDIT: The above code actually assumes 16 LEDs, if you have 15 LEDs just do:
LED= (1U << (adc >> 4)) - 1;

Related

speed up the AVR ISR

I am wondering if it is possible to speed up the ISR without changing the prescaler.
I have a timer with 2 compare registers A and B.
COMPA is used for a PWM output from around 22% up to 100%. This has a fixed frequency and I am not allowed to change it at least not much.
Now I would like to use the COMPB with around 4 times the speed but with a fixed duty cycle of 50%.
If I set the OCIE0B bit in TIMSK0 for the attiny13 can I do the following to speed things up?
Or am I misunderstanding something here?
ISR(TIM0_COMPB_vect){
switch (timing){
case 0:
OCR0B = 63;
PORTB ^= (1 << PB3);
timing = 1;
break;
case 1:
OCR0B = 127;
PORTB ^= (1 << PB3);
timing = 2;
break;
case 2:
OCR0B = 191;
PORTB ^= (1 << PB3);
timing = 3;
break;
case 3:
OCR0B = 255;
PORTB ^= (1 << PB3);
timing = 0;
break;
}
}
Any help appreciated.
Thanx :D
You can do this very efficiently by creatively using the Normal Mode.
The trick is to set the prescaller to get a clock period that is double what you want the variable-duty PWM signal to run at. So if, for example, you want that to PWM at 1Mhz, set the prescaller to 2Mhz.
Assume the variable duty cycle PWM is on pin A and the fixed 50% 4x clock signal is on pin B. (You can also swap these and and also update the code everything will still work)
Enable interrupts for "On compare match B" and "Overflow".
Force pin A high with a force compare match. (Alternately you can skip this step and instead use the inverse of the desired duty cycle in step 7)
Set the COM bits for 'A' to Toggle on match.
Leave the COM bits for B to off. Assumes you have DDR set for this pin to be normal GPIO.
Set the OCR for B to 128.
Set the WGM timer mode to 0 - "Normal Mode".
Set the OCR for A whatever you want the variable duty cycle to be. Note that you might need to special case here for extreme values of 0 and/or 255 depending on what you want to have happen (just turn the pin ON of OFF with GPIO).
You can repeat step 6 anytime you want to change the duty cycle of A and it will update on the next TOP.
Once you do these steps, the A pin will output the desired duty cycle at 1/2 the prescaller clock and the B will output 50% duty at 2x the prescaller clock (which is the desired 4x of the A period).
Here is the ISR code (note that I am not sure what the TOV vector is called in the attiny13 headers [it is sometimes different across AVRs] so you might have to edit the TIM0_OVF_vect name)...
ISR(TIM0_COMPB_vect,TIM0_OVF_vect){
PINB |= (1 << PB3); // Compiles to a single cycle SBI
}
See how this works?
Note that setting a bit in the PIN register actually toggles the PORT bit. This is a quirk of the AVR GPIOs that is documented in the datasheets.
Hopefully this is fast enough. If you really want to squeeze every last cycle out, you can even potentially save the 2 cycles of the RJMP from the interrupt vector by putting the single SBI instruction that the ISR compiles down to directly into the interrupt vector table with a trailing RETI, but this is more complicated!
Focusing solely on the C code aspects, then this can be trivially optimized as:
ISR(TIM0_COMPB_vect)
{
static const uint8_t OCR[4] = {63,127,191,255};
OCR0B = OCR[timing];
PORTB ^= 1u << PB3;
timing++;
if(timing==4)
timing=0;
}
Disassembled on gcc AVR -O3 (with all variables/registers volatile) this brings down the amount of instructions from ~50 to ~20, so it's about twice as fast and takes less memory.
If you just want the fastest equivalent version of the supplied code, then here it is...
ISR(TIM0_COMPB_vect){
OCR0B += 64;
PINB |= (1 << PB3);
}
OCR0B will overflow every 4 passes, which is defined behavior. Probably wise to initialize OCR0B to some non-zero number like 1 to avoid edge cases.
This avoids all variables and memory access - only register access.
Avoids all compares and braches.
The PINB method of toggling the pin compiles down to a single SBI instruction rather than a PUSH, load, XOR, store, POP sequence.
...but again, none of this matters if it does not work and unless you are using one of the two "immediate OCR update" modes, then updating OCR in the middle of a timer cycle will have no effect.

Getting data from MMA7455L sensor i2c

I succesfully connected the MMA-7455L sensor and I am getting data from it.
Although I have one question if someone can help me.
Can someone help me understand this piece of code? That I am using to get the data from.
i2cbuf[1] = 0x00;
HAL_I2C_Master_Receive(&hi2c1, 0x1D<<1, &i2cbuf[1], 6, 10);
ax = -(i2cbuf[1]<<8 | i2cbuf[2]);
ay = -(i2cbuf[3]<<8 | i2cbuf[4]);
az = -(i2cbuf[5]<<8 | i2cbuf[6]);
I am getting data and the outpit is in 8 bit. I understand that I am combining two 8 bit responses to make it a 16 bit response. But what I do not understand is the minus part.
Thank you in advance
My guess is that the values returned are 16 bit signed integers (int16_t) so you will have readings of -32767 through 0 to +32767.
Whoever designed the board with the accelerometer example code you're using, wanted the values to read correctly in the boards normal orientation, so they have negated the results
eg: -(i2cbuf[1]<<8 | i2cbuf[2]);
If both i2c values are 0xFF you get 0xFFFF, which is -1 (if type is int16)
Negate that and you get +1 which should indicate a positive acceleration to the application

UART communication on Atmega32A with PC

I'm a begginer in programming AVR microcontroler and I get a lot of headacke sometimes from reading the datasheets.
I'm trying to make a communication between my AVR and PC just to send some caracters and receive it on my computer.
There are two lines I don't understand from the whole program and that is:
void USART_init(void)
{
UBRRH = (uint8_t)(BAUD_PRESCALLER>>8); <---- this one!
UBRRL = (uint8_t)(BAUD_PRESCALLER); <--- and this one
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<UCSZ0)|(1<<UCSZ1)|(1<<URSEL);
}
Datasheet
Why do I have to shift BAUD_PRESCALLER with 8? If BAUD_PRESCALLER is a number and shifting that number with 8 doesn't mean the result will be zero?(Because we are shifting it too many times)
From the datasheet I understand that UBRRH contains the four most significant bits and the UBRRL contains the eight least signicant bits of the USART baut rate.(Note:UBBR is a 12-bit register)
So how actually we put all the required numbers in the UBBR register?
You have to shift it right 8 bits because the result of BAUD_PRESCALLER is larger than 8 bits. Shifting it right 8 bits gives you the most significant byte of a 16-bit value.
For example, if the value of BAUD_PRESCALAR is 0x123 - then 0x1 would be assigned to UBRRH and 0x23 would be assigned to UBRRL.
If the library was smart it could also perform sanity checking on the BAUD_PRESCALAR to make sure it fits in 16bits. If it can't, that means you cannot achieve the baud rate you want given the clock you are using. If you're UBRRx is truly 12bits, the sanity check would look something like this:
#if BAUD_PRESCALAR > 0xFFF
#error Invalid prescalar
#endif

Explain the C code instruction

The code below is used for programming microcontrollers. I want to know what the code below is doing. I know that '|' is OR and '&' AND but what is the whole line doing?
lcd_port = (((dat >> 4) & 0x0F)|LCD_EN|LCD_RS);
It's hard to put into context since we don't know what dat contains, but we can see that:
The data is right-shifted by 4 bits, so 11111111 becomes 00001111, for instance.
That value is AND'ed with 0x0F. This is a common trick to remove unwanted bits, since b & 1 = 1 and b & 0 = 0. Think of your number as a sequence of bits, here's a 2-byte example :
0011010100111010
&
0000000000001111
0000000000001010
Now the LCD_EN and LCD_RS flags are OR'ed. Again, this is a common binary trick, since b | 1 = 1 and b | 0 = b, so you can add flag but not remove them. So, if say LCD_EN = 0x01 and LCD_RS = 0x02,
0000000000001010
|
0000000000000011
0000000000001011
Hope that's clearer for you.
Some guesses, as you'll probably need to find chip datasheets to confirm this:-
lcd_port is probably a variable that directly maps to a piece of memory-mapped hardware - likely an alphanumeric LCD display.
The display probably takes data as four-bit 'nibbles' (hence the shift/and operations) and the higher four bits of the port are control signals.
LCD_EN is probably an abbreviation for LCD ENABLE - a control line used on the port.
LCD_RS is probably an abbreviation for LCD READ STROBE (or LCD REGISTER SELECT) - another control line used on the port. Setting these bits while writing to the port probably tells the port the kind of operation to perform.
I wouldn't be at all surprised if the hardware in use was a Hitachi HD44780 or some derivative.
It appears to be setting some data and flags on the lcd_port. The first part applies the mask 0x0F to (dat >> 4) (shift dat right 4) which is followed by applying the LCD_EN flag and then LCD_RS flag.
It is shifting the variable data four bits to the right, then masking the value with the value 15. This results in a value ranging from 0-15 (four left-most bits). This result is binary ORd with the LCD_EN and LCD_RS flags.
This code is shifting the bits of dat 4 bits to the right and then using & 0x0F to ensure it gets only those 4 least significant bits. It's then using OR to find which bits exist in that value OR LCD_EN OR LCD_RS and assigning that value to lcd_port.

Problems in AVR C combining ADC readings to generate PWM output

I'm writing a program for an ATMega328P that will take readings from several ADC channels, combine them into a single signal and output this signal through PWM.
I've successfully backed off my ADC polling to 50Hz per channel using Single Conversion mode. I'm using Timer/Counter2 for PWM generation, and Timer/Counter1 for doing the calculations I need to do to set compare values for Timer/Counter2. This is the ISR for Timer/Counter1:
// Interrupt service routine called to generate PWM compare values
ISR(TIMER1_COMPA_vect)
{
// Grab most recent ADC reading for ADC0
uint32_t sensor_value_0 = adc_readings[0];
// Get current value for base waveform from wavetable stored in sinewave_data
uint32_t sample_value_0 = pgm_read_byte(&sinewave_data[sample_0]);
// Multiply these two values together
// In other words, use the ADC reading to modulate the amplitude of base wave
uint32_t sine_0 = (sample_value_0 * sensor_value_0) >> 10;
// Do the same thing for ADC2
uint32_t sensor_value_1 = adc_readings[1];
uint32_t sample_value_1 = pgm_read_byte(&sinewave_data[sample_1]);
uint32_t sine_1 = (sample_value_1 * sensor_value_1) >> 10;
// Add channels together, divide by two, set compare register for PWM
OCR2A = (sine_0 + sine_1) >> 1;
// Move successive ADC base waves through wavetable at integral increments
// i.e., ADC0 is carried by a 200Hz sine wave, ADC1 at 300Hz, etc.
sample_0 += 2;
sample_1 += 3;
// Wrap back to front of wavetable, if necessary
if (sample_0 >= sinewave_length) {
sample_0 = 0;
}
if (sample_1 >= sinewave_length) {
sample_1 = 0;
}
} // END - Interrupt service routine called to generate PWM compare values
My problem is that that I get no PWM output. If I set either sensor_value_0 or sensor_value_1 to 1024 and leave the other sensor_value_ set to read from the ADC, I do get one full-amplitude component wave, and an amplitude-modulated component wave. If however, I choose a different value for the hardcoded, mock amplitude, I am not so lucky (1023, for instance). Any other values give me no PWM output. If I set both sensor_value_s to look at the same ADC channel, I would expect two component waves whose amplitudes are modulated identically. Instead, I get no PWM output. What is most confusing of all to me is that if I choose a value for the hardcoded amplitude that is an exact power of two, all is well.
The whole power-of-two part makes this seem to me to be a bit-twiddling issue that I'm not seeing. Can you see what I must have clearly missed? I'd appreciate any tips at all!
(I've posted my entire source here to keep things as neat as possible on SO.)
Your issue may be caused by the architecture of the AVR that you're developing on. The ATMega328p has 8 bit registers, similar to most other AVR chips. This means that the 32b values that you're working with must be stored in memory by the compiler and broken up into four separate registers every time you perform arithmetic on them. In fact, there are no arithmetic instructions that perform on more than one register at once, so I'm really not sure what the compiler is doing!
I'd be interested to know what the disassembly of your code is, but my guess is that gcc is using the MUL instruction to execute the sample_value_0 * sensor_value_0 code. This instruction operates on two 8b values and produces a 16b value, so I wouldn't be surprised if the reason you're seeing an odd dependence on multiples of two produce results.
I'd say try reworking this block of code by changing the data types of the variables. Use uint8_t for sensor_value_* and sample_value_*, and uint16_t for sine_*. Then, to make sure everything fits in the 8b OCR2A register, change the assignment to something like:
OCR2A = (sine_0 + sine_1) & 0xFF;
#Devrin, I appreciate the response, but just manipulating types didn't do it for me. Here's what I ended up doing:
uint8_t sine_0 = (pgm_read_byte(&sinewave_data[sample_0]) >> 5) * (adc_readings[1] >> 5);
uint8_t sine_1 = (pgm_read_byte(&sinewave_data[sample_1]) >> 5) * (adc_readings[2] >> 5);
OCR2A = (sine_0 >> 1) + (sine_1 >> 1);
Essentially, I've done all my shifting immediately, instead of waiting until the last minute. Unfortunately, I lose a lot of precision, but at least the code works as expected. Now, I wil begin cranking things back up to find the initial cause of my issues.

Resources