MSP430 Real time clock (RTC_B) doesn't work. Cannot write date/time registers - microcontroller

I'm trying to set date/time registers using the RTC_B module of MSP430F5338 microcontroller.
I'm doing it like this:
RTCCTL0 = 0;
RTCCTL1 |= RTCHOLD +RTCBCD;
RTCHOUR = 0x14;
RTCCTL1 &= ~RTCHOLD;
It doesn't work, and simply ignore the assignements. I cannot understand why. The only strange thing I've noticed is the RTCOFIFG flag set.
Any idea?
Addendum
This is how I set up clock sources:
void clk_init(){
SetVcoreUp (0x01);
SetVcoreUp (0x02);
SetVcoreUp (0x03);
UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
UCSCTL4 |= SELA_2; // Set ACLK = REFO
__bis_SR_register(SCG0); // Disable the FLL control loop
UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
UCSCTL1 = DCORSEL_7; // Select DCO range 50MHz operation
UCSCTL2 = FLLD_1 | ((f_SMCLK/f_ACLK) -1); // Set DCO Multiplier for 25MHz
// (N + 1) * FLLRef = Fdco
// (762 + 1) * 32768 = 25MHz
// Set FLL Div = fDCOCLK/2
__bic_SR_register(SCG0); // Enable the FLL control loop
// Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
do{
UCSCTL7 &= ~(XT2OFFG | XT1LFOFFG | DCOFFG);
// Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
}
void SetVcoreUp (unsigned int level)
{
// Open PMM registers for write
PMMCTL0_H = PMMPW_H;
// Set SVS/SVM high side new level
SVSMHCTL = SVSHE | SVSHRVL0 * level | SVMHE | SVSMHRRL0 * level;
// Set SVM low side to new level
SVSMLCTL = SVSLE | SVMLE | SVSMLRRL0 * level;
// Wait till SVM is settled
while ((PMMIFG & SVSMLDLYIFG) == 0);
// Clear already set flags
PMMIFG &= ~(SVMLVLRIFG | SVMLIFG);
// Set VCore to new level
PMMCTL0_L = PMMCOREV0 * level;
// Wait till new level reached
if ((PMMIFG & SVMLIFG))
while ((PMMIFG & SVMLVLRIFG) == 0);
// Set SVS/SVM low side to new level
SVSMLCTL = SVSLE | SVSLRVL0 * level | SVMLE | SVSMLRRL0 * level;
// Lock PMM registers for write access
PMMCTL0_H = 0x00;
}

I've SOLVED adding this before clock setup:
while (BAKCTL & LOCKBAK) BAKCTL &= ~LOCKBAK;
Basically this is due to the fact that msp430f5338 has the battery backup system, so you'll need this code before you set XT1 drive ACLK.
Hope this helps.

Having just had a browse through the datasheet - two things:
By setting the RTCBCD flag in RTCCTL1, you're saying you want to use binary-coded decimal, so setting RTCHOUR as 0x0A is nonsense. To write proper BCD for, say, 14:47 (2:47pm), you write the hours as 0x14 and 0x47 as the minutes, i.e., write as you see.
Ensure you're not in low power mode 5 (LPM5) - configuration settings are not retained.
Addendum:
Also, the RTCOFIFG flags says you had a fault with your oscillator, so confirm your circuitry too.

Related

setting right ADC prescaler on the Arduino Due in timer and interrupt driven multi-channel ADC acquisition

I am trying to follow, adapt, understand (and clean up a bit) a variation around the code available there, for the Arduino Due: https://forum.arduino.cc/index.php?topic=589213.0 . I do not like the forum format, as things end up buried deep, so asking here instead. Unfortunately this means that there is quite a lot of explanations before the question. If you think this is wrong to post it here, let me know, and I can move.
Basically, the idea is to log several ADC channels in a buffer, using timer-based triggering. There is a bit of setup:
// sample rate in Hz
constexpr int sample_rate = 1000;
constexpr uint8_t channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_channels = sizeof(channels);
Then time counter 0 channel 2 is set at the right frequency for triggering the ADC conversion:
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
Finally this can be used to trigger the ADC:
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several channels in a row one after the other
// report finished conversion using ADC interrupt
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(1) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << channels[nbr_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
and the ADC output can be captured in the corresponding ISR:
void ADC_Handler() {
for (size_t i = 0; i < nbr_channels; i++)
{
SOME_BUFFER[i] = static_cast<volatile uint16_t>( * (ADC->ADC_CDR + channels[i]) & 0x0FFFF ); // get the output
}
}
I think this is quite understandable, but I have one question: the setting of the pre-scaler.
if I understand well discussions online, the pre-scaler should be set so that frq_ADC >= sample_rate * nbr_channels, basically because the chip is just multiplexing the ADC through several channels
if I understand well, we want to set such pre-scaler value as high as possible given the previous constraint, so that the ADC frequency is as low as possible, because this improves ADC conversion quality
Is that right?
The problem is that I am confused about how to set the pre-scaler, and what value corresponds to what, because what I find in the datasheet disagree with some other online responses I read.
From the datasheet https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf : "The ADC clock range is between MCK/2, if PRESCAL is 0, and MCK/512, if PRESCAL is set to 255 (0xFF).". This is consistent with what I find on page 1334: "ADCClock = MCK / ( (PRESCAL+1) * 2 )". But page 1318, it is written that the conversion rate is 1MHz. Then how is that compatible with having a MCK frequency of 84MHz on the Due? 84/2 = 48MHz, 84/512 = 0.164MHz, the high freq value is too high.
Then to add to the confusion I have found this issue: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due/21054#21054 that also seem to conflict with the 1MHz upper bound.
Any idea where I misunderstand something? (and any more comments around the general working of the program?).
Ok, so I did some tests with the code, checking when I was missing some conversions depending on the timer frequency and the prescaler value. The code is a bit long, so I post it at the end of the answer. Basically:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
I think this indicates that:
the pre-scaler value is well an 8 bits int, between 0 and 255, as it wraps up at 256
I have difficultie matching the results to the formula in the datasheet. I guess this is because there is some overhead switching channels etc (?). For example:
the results are consistent with ```ADC_freq = 1MHz / ( ps ) at the highest frequencies, but I suppose this is because there is a bit of overhead switching channels
the results are consistent with ```ADC_freq = 2MHz / ( ps ) at 10 kHz, and at 1kHz, even using the highest prescaler is fine.
The code I was using is the following, and the criterion for deciding that things fail is that the code reports a drop in the effective sample frequency over the 5 channels:
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// timer driven ADC convertion captured by interrupt on n adc_channels for Arduino Due
//
// this is for Arduino Due only!
//
// the interrupt based ADC measurement is adapted from:
// https://forum.arduino.cc/index.php?topic=589213.0
// i.e. adc_setup(), tc_setup(), ADC_handler() are inspired from the discussion there.
//
// written with VSCode + Platformio and Due board setup
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// make my linter happy
#include "Arduino.h"
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some vital ADC grabbing setup
// sample rate in Hz, should be able to go up to several 10s ok kHz at least
constexpr int adc_sample_rate = 1000;
// size of the data buffers "in time"
// i.e. how many consecutive measurements we buffer for each channel
constexpr size_t adc_buffer_nbr_consec_meas = 5;
// the adc_channels to read, in uC reference, NOT in Arduino Due pinout reference
// for a mapping, see: https://components101.com/microcontrollers/arduino-due
// i.e. A0 is AD7
// A1 AD6
// A2 AD5
// A3 AD4
// A4 AD3
// A5 AD2
// A6 AD1
// A7 AD0
constexpr uint8_t adc_channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_adc_channels = sizeof(adc_channels);
// the buffer containing the measurements for all adc_channels over several measurements in time
volatile uint16_t adc_meas_buffer[adc_buffer_nbr_consec_meas][nbr_adc_channels];
// flag when a full vector of conversions is available
volatile bool adc_flag_conversion = false;
// time index of the current measurement in the adc reads buffer
volatile size_t crrt_adc_meas_buffer_idx = 0;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some non-vital printing config
// a bit of time tracking, just to analyze how good performance
unsigned long current_us = 0;
unsigned long previous_us = 0;
unsigned long delta_us = 0;
float delta_us_as_s = 0;
float delta_us_as_ms = 0;
int nbr_readings_since_reduced_time_stats = 0;
unsigned long current_reduced_time_stats_us = 0;
unsigned long previous_reduced_time_stats_us = 0;
float delta_reduced_time_stats_us_as_s = 0;
float effective_logging_frequency = 0;
// decide what to print on serial
constexpr bool print_reduced_time_stats = true;
constexpr bool print_time_stats = false;
constexpr bool print_full_buffer = false;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// low level functions for setting clock and ADC
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several adc_channels in a row one after the other
// report finished conversion using ADC interrupt
// tests about pre-scaler: formula should be:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
// CCL: use ps 2 at 100kHz with 5 channels, 20 at 10kHz, 200 at 1kHz
void adc_setup()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(200) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
// unclear, asked: https://stackoverflow.com/questions/64243073/setting-right-adc-prescaler-on-the-arduino-due-in-timer-and-interrupt-driven-mul
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_adc_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << adc_channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << adc_channels[nbr_adc_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to adc_sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup()
{
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / adc_sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
// ISR for the ADC ready readout interrupt
// push the current ADC data on all adc_channels to the buffer
// update the time index
// set flag conversion ready
void ADC_Handler()
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
adc_meas_buffer[crrt_adc_meas_buffer_idx][i] = static_cast<volatile uint16_t>(*(ADC->ADC_CDR + adc_channels[i]) & 0x0FFFF);
}
crrt_adc_meas_buffer_idx = (crrt_adc_meas_buffer_idx + 1) % adc_buffer_nbr_consec_meas;
adc_flag_conversion = true;
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// a simple script: setup and print information
void setup()
{
Serial.begin(115200);
delay(100);
adc_setup();
tc_setup();
}
void loop()
{
if (adc_flag_conversion == true)
{
adc_flag_conversion = false;
if (print_reduced_time_stats)
{
nbr_readings_since_reduced_time_stats += 1;
if (nbr_readings_since_reduced_time_stats == adc_sample_rate)
{
current_reduced_time_stats_us = micros();
delta_reduced_time_stats_us_as_s = static_cast<float>(current_reduced_time_stats_us - previous_reduced_time_stats_us) / 1000000.0;
effective_logging_frequency = static_cast<float>(adc_sample_rate) / delta_reduced_time_stats_us_as_s;
previous_reduced_time_stats_us = current_reduced_time_stats_us;
Serial.print(F("Effective logging freq over nbr spls that should correspond to 1 second: "));
Serial.println(effective_logging_frequency);
nbr_readings_since_reduced_time_stats = 0;
}
}
if (print_time_stats)
{
current_us = micros();
delta_us = current_us - previous_us;
delta_us_as_s = static_cast<float>(delta_us) / 1000000.0;
delta_us_as_ms = static_cast<float>(delta_us) / 1000.0;
Serial.println(F("ADC avail at uS"));
Serial.println(micros());
Serial.println(F("elapsed us"));
Serial.println(delta_us);
Serial.println(F("elapsed ms"));
Serial.println(delta_us_as_ms);
Serial.println(F("elapsed s"));
Serial.println(delta_us_as_s);
Serial.println(F("updated idx:"));
size_t last_modified_buffer_idx;
if (crrt_adc_meas_buffer_idx > 0){
last_modified_buffer_idx = crrt_adc_meas_buffer_idx - 1;
}
else{
last_modified_buffer_idx = nbr_adc_channels - 1;
}
Serial.println(last_modified_buffer_idx);
previous_us = current_us;
}
if (print_full_buffer)
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
Serial.print(F(" ADC "));
Serial.print(adc_channels[i]);
Serial.println(F(" meas in time:"));
for (size_t j = 0; j < adc_buffer_nbr_consec_meas; j++)
{
Serial.print(adc_meas_buffer[j][i]);
Serial.print(F(" "));
}
Serial.println();
}
}
}
}

Is there any register to set the clock frequency for UART?

I am working with UART in LPC2148 micro controller.I could not able to find any register to set the clock frequency for the UART.Is there any register to set the clock frequency?
The "quick and dirty" method described in the reference is:
U0DLL = PCLK in Hertz / ( 16 * desired_baud_rate )
This is a simplified equation; the others have adjustments that can be made by adjusting various registers to fine-tune the serial baud rate.
From the same reference:
Now , as seen in Ex. 2 – in order to get 9600(9605 actually) bauds at 60Mhz PCLK we must use the following settings for baud generation :
U0DLL = 110 ; U0DLM = 1 ; MULVAL = 15 ; DIVADDVAL = 0
(You can convert these values to Hex =or= direclty use it in decimal form)
Now , lets make a function named “InitUART0()” which will configure and initialize UART0 as required :
void InitUART0(void)
{
PINSEL0 = 0x5; /* Select TxD for P0.0 and RxD for P0.1 */
U0LCR = 3 | (1<<7) ; /* 8 bits, no Parity, 1 Stop bit | DLAB set to 1 */
U0DLL = 110;
U0DLM = 1;
U0FDR = (MULVAL<<4) | DIVADDVAL; /* MULVAL=15(bits - 7:4) , DIVADDVAL=0(bits - 3:0) */
U0LCR &= 0x0F; // Set DLAB=0 to lock MULVAL and DIVADDVAL
//BaudRate is now ~9600 and we are ready for UART communication!
}

“Read Analog Voltage” sample rate

I want to make sure my code looks like working, since I don't have a lot of time with a signal generator tomorrow and I want to know how to set the sample rate.
I want to sample a 2kHz signal with a samplerate of 6kHz with a Arduino MEGA 2560.
It, doesn't have to be in real time, so i'm thinking of filling a buffer and then sending those over the serial connection.
Can anyone say if this code defenitly wouldn't work for this?
And how could i set the samplerate to 6kHz?
void setup() {
Serial.begin(9600);
}
void loop() {
for(int x = 0; x < 1000; x++){
// read the input on analog pin 0:
int sensorValue[x] = analogRead(A0);
}
for( x = 0; x < 1000; x++){
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage[x] = sensorValue[x] * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage[x]);
}
}
Thank you.
Well, as I've mentioned in another thread, you can use auto triggering mode of ADC (for UNO and ATMega328p based Arduinos):
void setup() {
Serial.begin(256000);
// ADC setup is done by arduino framework, but it's possible to change it slightly (for ATMega328) :
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); // ADTS2..0 = 111, Timer 1 input capture event trigger source
ADCSRA |= _BV(ADATE); // enable auto trigger mode
ADCSRA |= _BV(ADIF); // reset conversion end flag (= interrupt flag)
// timer 1 setting:
TCCR1A = 0; // clear all
ICR1 = F_CPU/6000U; // 1 should be substracted here but result is about 2665.7 and it will be truncated to 2665
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // CTC mode with ICR1 as TOP value, enabled with no prescaling
TIMSK1 = _BV(ICF1); // not working without this... Flag must be cleaned up after the trigger ADC, otherwise it's stucked
analogRead(A0); // dummy read to set correct channel and to start auto trigger mode
pinMode(13, OUTPUT);
}
void loop() {
if (ADCSRA & _BV(ADIF)) {
ADCSRA |= _BV(ADIF); // reset flag by writing logic 1
Serial.println(ADC);
}
}
ISR(TIMER1_CAPT_vect) { // to clear flag
PINB = _BV(PB5); // and toggle d13 so frequency can be measured (it'd be half of real rate)
// it might be enabled on PWM pin too by setting force output compare and some compare register to half of value ICR1
}
This sketch uses baud rate 250000 but it's still too slow. The space character can be used as an separator, this'll save one character (as new line are usually two characters: \r\n). One value can be 1 to 4 characters long so for values:
0-9 - 3B you need baud rate 3*10*6000 = 180000
10-99 - 4B and you need baud rate 240000
and for the rest of cases you're too slow.
So the only way is sending those integers binary and without separator it'd be even better. The 2B per value results into minimal baud rate around 120000 baud/s.

generate arbitrary PWM signal using ATMEGA128

I am well familiar with PWM generation in Atmega128 and its family microcontrollers. I have been using prescalar and other registers for generating frequency. But I have to generate 20KHz pwm signal. I tried but I could not get the desired output. Can anyone suggest me or help me how to do it ?
As far as I know, in atmega128, 1 instruction takes 1 cycle. Using 16MHz crystal, 1 instruction completes in 1/16M sec.
I tried to generate 20Khz signal (50 us)with 25us duty cycle. But I get different frequency (277.78 Hz) in oscilloscope which is far less than 20KHz
My calculation was
16MH = 20000Hz * 800.
for 0-399 count, I made port high and
399-799 count, I made port low.
void frequency(void){ // 20kHz Frequency
if (cnt1 <= 399){
PORTB |= (1<<7);
} else {
PORTB &= ~(1<<7);
}
cnt1++;
if (cnt1 >= 800) cnt1 = 0;
}
I don't have access to the 128 but verified its 16-bit Timer 1 is similar to that in the 328 and 32U4 so the following should work with minor modification (the main sticking point is probably looking up what pin the overflow register is bound to):
#include <avr/io.h>
#include <util/delay.h>
struct CTC1
{
static void setup()
{
// CTC mode with TOP-OCR1A
TCCR1A = 0;
TCCR1B = _BV(WGM12);
// toggle channel A on compare match
TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0);
// set channel A bound pin PB1 to output mode
#if defined(__AVR_ATmega32U4__)
DDRB |= _BV(5);
#else
DDRB |= _BV(1);
#endif
}
static void set_freq(float f)
{
static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256);
uint16_t n;
if (f >= f1) n = 1;
else if (f >= f8) n = 8;
else if (f >= f64) n = 64;
else if (f >= f256) n = 256;
else n = 1024;
prescale(n);
OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1));
}
static void prescale(uint16_t n)
{
uint8_t bits = 0;
switch (n)
{
case 1: bits = _BV(CS10); break;
case 8: bits = _BV(CS11); break;
case 64: bits = _BV(CS11) | _BV(CS10); break;
case 256: bits = _BV(CS12); break;
case 1024: bits = _BV(CS12) | _BV(CS10); break;
default: bits = 0;
}
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits;
}
static inline float min_freq(uint16_t n)
{
return ceil(F_CPU / (2 * n * 65536));
}
};
void setup()
{
CTC1::setup();
CTC1::set_freq(20e3);
}
void loop()
{
// do whatever
_delay_ms(1);
}
int main()
{
setup();
for (;;)
loop();
}
I tested on my scope and measure exactly 20kHz off a 328p running at 16MHz. If 20kHz is the only frequency you need then you can simplify this substantially. Translating the code to use one of the 8-bit timers is also straightforward though I haven't verified that it's possible to hit exactly 20kHz with those.
It's not a good idea to use counter in C to implement the PWM or anything time critical really. Although C converts your code to specific machine code, you don't really know how much time it will take.
Your code does not translate to:
make port B high 400 times (PORTB |= (1<<7);)
make port B low 400 times (PORTB &= ~(1<<7);)
, but rather something like this (simplification, human-readable):
load variable cnt1 to memA;
load 399 to memB
compare mem A to memB
put result to memC
if memC eq "somthing indicating <=" do PORTB |= (1<<7);
if memC something else do PORTB &= ~(1<<7);
load cnt1 to memD and increment;
write memD to cnt1;
load 800 to memE
load cnt1 to memF
compare memF to memE
put result to memG
if memG eq "somthing indicating <=" do memF = 0, write memF to cnt1;
if memG something else go to start;
If you look at this from "C" point of view you need to do at least:
1. comare cnt1-399
2. if ok - do / else
3. port high / port low
4. add one to cnt1
5. compare cnt1 and 800
It then depends on you compiler how good it is at optimizing all the loads and writes (usually quite good).
You can have control on what the delays will be if you really know your compiler and don't use to much optimization (it is usually to complex to follow) or by writing the code in assembler. But then you will have to use logic similar to my explanation of the machine code (assembler is close to human-readable machine code).
I think the solution for you are timer interrupts. There's a good tutorial for atmega128 this here.
Also what do you mean with:
I tried to generate 20Khz signal (50 us)with 25us duty cycle.
Do you mean 20kHz signal with 50% duty cycle? so 25us low, 25 us high?
If this is the case you can do this with one timer interrupt and one (binary) counter.
Exactly the "8 bit timer example" you can read about in the provided link.

MSP430 not able to handle double

I am trying to program a MSP430 with a simple "FIR filter" program, that looks like the following:
#include "msp430x22x4.h"
#include "legacymsp430.h"
#define FILTER_LENGTH 4
#define TimerA_counter_value 12000 // 12000 counts/s -> 12000 counts ~ 1 Hz
int i;
double x[FILTER_LENGTH+1] = {0,0,0,0,0};
double y = 0;
double b[FILTER_LENGTH+1] = {0.0338, 0.2401, 0.4521, 0.2401, 0.0338};
signed char floor_and_convert(double y);
void setup(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_8MHZ; // Set DCO
DCOCTL = CALDCO_8MHZ;
/* Setup Port 3 */
P3SEL |= BIT4 + BIT5; // P3.4,5 = USART0 TXD/RXD
P3DIR |= BIT4; // P3.4 output direction
/* UART */
UCA0CTL1 = UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 9600 baud from 8Mhz
UCA0BR1 = 0x3;
UCA0MCTL = UCBRS_2;
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
/* Setup TimerA */
BCSCTL3 |= LFXT1S_2; // LFXT1S_2: Mode 2 for LFXT1 = VLO
// VLO provides a typical frequency of 12kHz
TACCTL0 = CCIE; // TACCR0 Capture/compare interrupt enable
TACCR0 = TimerA_counter_value; // Timer A Capture/Compare 0: -> 25 Hz
TACTL = TASSEL_1; // TASSEL_1: Timer A clock source select: 1 - ACLK
TACTL |= MC_1; // Start Timer_A in up mode
__enable_interrupt();
}
void main(void) // Beginning of program
{
setup(); // Call Function setup (see above)
_BIS_SR(LPM3_bits); // Enter LPM0
}
/* USCIA interrupt service routine */
/*#pragma vector=USCIAB0RX_VECTOR;*/
/*__interrupt void USCI0RX_ISR(void)*/
interrupt (USCIAB0RX_VECTOR) USCI0RX_ISR(void)
{
TACTL |= MC_1; // Start Timer_A in up mode
x[0] = (double)((signed char)UCA0RXBUF); // Read received sample and perform type casts
y = 0;
for(i = 0;i <= FILTER_LENGTH;i++) // Run FIR filter for each received sample
{
y += b[i]*x[i];
}
for(i = FILTER_LENGTH-1;i >= 0;i--) // Roll x array in order to hold old sample inputs
{
x[i+1] = x[i];
}
while (!(IFG2&UCA0TXIFG)); // Wait until USART0 TX buffer is ready?
UCA0TXBUF = (signed char) y;
TACTL |= TACLR; // Clear TimerA (prevent interrupt during receive)
}
/* Timer A interrupt service routine */
/*#pragma vector=TIMERA0_VECTOR;*/
/*__interrupt void TimerA_ISR (void)*/
interrupt (TIMERA0_VECTOR) TimerA_ISR(void)
{
for(i = 0;i <= FILTER_LENGTH;i++) // Clear x array if no data has arrived after 1 sec
{
x[i] = 0;
}
TACTL &= ~MC_1; // Stops TimerA
}
The program interacts with a MatLab code, that sends 200 doubles to the MSP, for processing in the FIR filter. My problem is, that the MSP is not able to deal with the doubles.
I am using the MSPGCC to compile the code. When I send a int to the MSP it will respond be sending a int back again.
Your problem looks like it is in the way that the data is being sent to the MSP.
The communications from MATLAB is, according to your code, a sequence of 4 binary byte values that you then take from the serial port and cast it straight to a double. The value coming in will have a range -128 to +127.
If your source data is any other data size then your program will be broken. If your data source is providing binary "double" data then each value may be 4 or 8 bytes long depending upon its internal data representation. Sending one of these values over the serial port will be interpreted by the MSP as a full set of 4 input samples, resulting in absolute garbage for a set of answers.
The really big question is WHY ON EARTH ARE YOU DOING THIS IN FLOATING POINT - on a 16 bit integer processor that (many versions) have integer multiplier hardware.
As Ian said, You're taking an 8bit value (UCA0RXBUF is only 8 bits wide anyway) and expecting to get a 32bit or 64 bit value out of it.
In order to get a proper sample you would need to read UCA0RXBUF multiple times and then concatenate each 8 bit value into 32/64 bits which you then would cast to a double.
Like Ian I would also question the wisdom of doing floating point math in a Low power embedded microcontroller. This type of task is much better suited to a DSP.
At least you should use fixed point math, seewikipedia (even in a DSP you would use fixed point arithmetic).
Hmm. Actually the code is made of my teacher, I'm just trying to make it work on my Mac, and not in AIR :-)
MATLAB code is like this:
function FilterTest(comport)
Fs = 100; % Sampling Frequency
Ts = 1/Fs; % Sampling Periode
L = 200; % Number of samples
N = 4; % Filter order
Fcut = 5; % Cut-off frequency
B = fir1(N,Fcut/(Fs/2)) % Filter coefficients in length N+1 vector B
t = [0:L-1]*Ts; % time array
A_m = 80; % Amplitude of main component
F_m = 5; % Frequency of main component
P_m = 80; % Phase of main component
y_m = A_m*sin(2*pi*F_m*t - P_m*(pi/180));
A_s = 40; % Amplitude of secondary component
F_s = 40; % Frequency of secondary component
P_s = 20; % Phase of secondary component
y_s = A_s*sin(2*pi*F_s*t - P_s*(pi/180));
y = round(y_m + y_s); % sum of main and secondary components (rounded to integers)
y_filt = round(filter(B,1,y)); % filtered data (rounded to integers)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Serial_port_object = serial(comport); % create Serial port object
set(Serial_port_object,'InputBufferSize',L) % set InputBufferSize to length of data
set(Serial_port_object,'OutputBufferSize',L) % set OutputBufferSize to length of data
fopen(Serial_port_object) % open Com Port
fwrite(Serial_port_object,y,'int8'); % send out data
data = fread(Serial_port_object,L,'int8'); % read back data
fclose(Serial_port_object) % close Com Port
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
subplot(2,1,1)
hold off
plot(t,y)
hold on
plot(t,y_filt,'r')
plot(t,y_filt,'ro')
plot(t,data,'k.')
ylabel('Amplitude')
legend('y','y filt (PC)','y filt (PC)','y filt (muP)')
subplot(2,1,2)
hold off
plot(t,data'-y_filt)
hold on
xlabel('time')
ylabel('muP - PC')
figure(1)
It is also not advised to keep interrupt routines doing long processing routines, because you will impact on interrupt latency. Bytes comming from the PC can get easily lost, because of buffer overrun on the serial port.
The best is to build a FIFO buffer holding a resonable number of input values. The USCI routine fills the FIFO while the main program keeps looking for data inside it and process them as they are available.
This way, while the data is being processed, the USCI can interrupt to handle new incomming bytes.
When the FIFO is empty, you can put the main process in a suitable LPM mode to conserve power (and this is the best MSP430 feature). The USCI routine will wake the CPU up when a data is ready (just put the WAKEUP attribute in the USCI handler if you are using MSPGCC).
In such a scenario be sure to declare volatile every variable that are shared between interrupt routines and the main process.

Resources