I am having trouble setting up high speed PWM on my ATtiny85. I need to use the PCK, at a speed of 400 kHz. I believe that I have followed the data sheet correctly, but for some reason, the timer interrupt flags are not working.
If I program the device, the output of the corresponding pin is a constant 5 V.
If I comment out the PCK setup and use the system clock instead, the flags are correctly set and PWM works fine. The code is posted. Why aren't the flags setting and the PWM isn't working?
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
PORTB = 0; //Reset values on port B
// After setting up the timer counter,
// set the direction of the ports to output
DDRB |= (1<<PB1) | (1<<PB0); // Set the direction of PB1 to an output
// PLLCSR - PLL control and status register:
// PLL is a clock multiplier - multiplies system 8 MHz by 8 to 64 MHz
// PLL is enabled when:PLLE bit is enabled,
// CKSEL fuse is programmed to 0001. This clock is
// switched off in sleep modes!
PLLCSR |= (1<<PLLE); // PLL enable
// Wait until the PLOCK bit is enabled
// before allowing the PCK to be enabled
//WaitForPLOCK();
//unsigned int i = 0;
while ((PLLCSR & (1<<PLOCK)) == 0x00)
{
// Do nothing until plock bit is set
}
PLLCSR |= (1<<PCKE); // Enable asynchronous mode, sets PWM clock source
TCCR1 =
(1<<CTC1) | // Enable PWM
(1<<PWM1A) | // Set source to pck
(1<<(CS10)) | // Clear the pin when match with ocr1x
(1<<COM1A1);
GTCCR = (1<<PWM1B) | (1<<COM1B1);
// Set PWM TOP value - counter will count and reset
// after reaching this value
// OCR1C
// 400 kHz 159
// 450 kHz 141
// 500 kHz 127
OCR1C = 159;
// Enable Timer1 OVF interrupt
TIMSK = (1<<OCIE1A) | (1<<TOIE1);
sei();
// This should set the duty cycle to about 75%
OCR1A = 120;
The solution involved the CKDIV8 fuse. To program this fuse correctly however, HVSP "High Voltage Serial Programming" was required. After removing this fuse so that the device operated at 8 MHz, the PWM gave a 400 kHz output. I hope other people find this useful!
The errata (section 27 of the datasheet) states 'PLL will not lock under 6 MHz'. The only workaround listed is to set the clock to 6 MHz or higher.
Related
I am currently working on a low-power project using the Adafruit Feather M0 microprocessor. A requirement of my project is to be able to sleep the CPU and wake it again using an external interrupt triggered from the MPU6050 accelerometer.
I have tested the following code sample from GitHub - it works successfully! The question that I need answering is how to I alter this sample code to work on Pin 13 of the feather, rather than pin 6.
#define interruptPin 6
volatile bool SLEEP_FLAG;
void EIC_ISR(void) {
SLEEP_FLAG ^= true; // toggle SLEEP_FLAG by XORing it against true
//Serial.print("EIC_ISR SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(3000); // wait for console opening
attachInterrupt(digitalPinToInterrupt(interruptPin), EIC_ISR, CHANGE); // Attach interrupt to pin 6 with an ISR and when the pin state CHANGEs
SYSCTRL->XOSC32K.reg |= (SYSCTRL_XOSC32K_RUNSTDBY | SYSCTRL_XOSC32K_ONDEMAND); // set external 32k oscillator to run when idle or sleep mode is chosen
REG_GCLK_CLKCTRL |= GCLK_CLKCTRL_ID(GCM_EIC) | // generic clock multiplexer id for the external interrupt controller
GCLK_CLKCTRL_GEN_GCLK1 | // generic clock 1 which is xosc32k
GCLK_CLKCTRL_CLKEN; // enable it
while (GCLK->STATUS.bit.SYNCBUSY); // write protected, wait for sync
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN4; // Set External Interrupt Controller to use channel 4 (pin 6)
PM->SLEEP.reg |= PM_SLEEP_IDLE_CPU; // Enable Idle0 mode - sleep CPU clock only
//PM->SLEEP.reg |= PM_SLEEP_IDLE_AHB; // Idle1 - sleep CPU and AHB clocks
//PM->SLEEP.reg |= PM_SLEEP_IDLE_APB; // Idle2 - sleep CPU, AHB, and APB clocks
// It is either Idle mode or Standby mode, not both.
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Enable Standby or "deep sleep" mode
SLEEP_FLAG = false; // begin awake
// Built-in LED set to output and high
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin direction to output
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to high
Serial.println("Setup() Run!");
}
void loop() {
// put your main code here, to run repeatedly:
if (SLEEP_FLAG == true) {
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTCLR.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to low
Serial.println("I'm going to sleep now.");
__WFI(); // wake from interrupt
SLEEP_FLAG = false;
Serial.println("Ok, I'm awake");
Serial.println();
}
//Serial.print("SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // toggle output of built-in LED pin
delay(1000);
}
As per the pinout diagram and Atmel datasheet, I am struggling to work out which changes to make to allow pin 13 to operate in the same way as pin 6.
Atmel Datasheet
The obvious solution is to change the following lines...
#define interruptPin 13
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN1; // Set External Interrupt Controller to use channel 4 (pin 6)
I suspected channel 1 (WAKEUPEN1) due to the ENINT^1 next to pin 13 on the pinout diagram. But this didn't work, the code pin operation did not exhibit the same behaviour as the pin 6 setup.
I would be very grateful for any suggestion of how to implement this code working on Pin 13. Many thanks for your support.
I'm not an authority here, and your code looks correct to me.
Except, the pin out shows Pin 13 is the built-in LED line, and you manipulate LED_BUILTIN several places in your code. That's almost certainly conflicting with your attempts to use 13 as an interrupt line.
I'm trying to write my own driver for USART_TX on an STM32L476RG Nucleo Board.
Here the datasheet and the reference manual.
I'm using Keil uVision 5 and I set in the Manage dialog:
CMSIS > Core
Device > Startup
Xtal=16MHz
I want to create a single character transmitter. According to the manual instructions in Sec. 40 p 1332 I wrote this code:
// APB1 connects USART2
// The USART2 EN bit on APB1ENR1 is the 17th
// See alternate functions pins and label for USART2_TX! PA2 is the pin and AF7 (AFRL register) is the function to be set
#include "stm32l4xx.h" // Device header
#define MASK(x) ((uint32_t) (1<<(x)));
void USART2_Init(void);
void USART2_Wr(int ch);
void delayMs(int delay);
int main(void){
USART2_Init();
while(1){
USART2_Wr('A');
delayMs(100);
}
}
void USART2_Init(void){
RCC->APB1ENR1 |= MASK(17); // Enable USART2 on APB1
// we know that the pin that permits the USART2_TX is the PA2, so...
RCC->AHB2ENR |= MASK(0); // enable GPIOA
// Now, in GPIOA 2 put the AF7, which can be set by placing AF7=0111 in AFSEL2 (pin2 selected)
// AFR[0] refers to GPIOA_AFRL register
// Remember: each pin asks for 4 bits to define the alternate functions. see pg. 87
// of the datasheet
GPIOA->AFR[0] |= 0x700;
GPIOA->MODER &= ~MASK(4);// now ... we set the PA2 directly with moder as alternate function "10"
// USART Features -----------
//USART2->CR1 |=MASK(15); //OVER8=1
USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?
//USART2->BRR = 0x1A1; //This one works!!!
USART2->CR1 |=MASK(0); //UE
USART2->CR1 |=MASK(3); //TE
}
void USART2_Wr(int ch){
//wait when TX buffer is empty
while(!(USART2->ISR & 0x80)) {} //when data is transfered in the register the ISR goes 0x80.
//then we lock the procedure in a while loop until it happens
USART2->TDR =(ch & 0xFF);
}
void delayMs(int delay){
int i;
for (; delay>0; delay--){
for (i=0; i<3195; i++);
}
}
Now, the problem:
The system works, but not properly. I mean: if I use RealTerm at 9600 baud-rate, as configured by 0x683 in USART_BRR reg, it shows me wrong char but if I set 2400 as baud rate on real term it works!
To extract the 0x683 in USART_BRR reg i referred to Sec. 40.5.4 USART baud rate generation and it says that if OVER8=0 the USARTDIV=BRR. In my case, USARTDIV=16MHz/9600=1667d=683h.
I think that the problem lies in this code row:
USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?
because if I replace it as
USART2->BRR = 0x1A1; //USARTDIV=16Mhz/9600?
THe system works at 9600 baud rate.
What's wrong in my code or in the USARTDIV computation understanding?
Thank you in advance for your support.
Sincerely,
GM
The default clock source for the USART is PCLK1 (figure 15) PCLK1 is SYSCLK / AHB_PRESC / AHB1_PRESC. If 0x1A1 results in a baud rate of 9600, that suggests PCLK1 = 4MHz.
4MHz happens to be the default frequency of your processor (and PCLK1) at start-up when running from the internal MSI RC oscillator. So the most likely explanation is that you have not configured the clock tree, and are not running from the 16MHz HSE as you believe.
Either configure your clock tree to use the 16MHz source, or perform your calculations on the MSI frequency. The MSI precision is just about good enough over normal temperature range to maintain a sufficiently accurate baud rate, but it is not ideal.
Recently bought a new USB oscilloscope and tried to measure the frequency of avr timer0. There is 12 MHz crystal oscillator connected to atmega and timer0 set up to fast PWM mode without prescaler. Here is the code:
#include <avr/io.h>
int main(void)
{
DDRB = 0x08;
TCCR0 |= (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00);
OCR0 = 128;
while(1){
}
}
But on oscilloscope frequency equals 46.90 kHz.
Fuse bits are on the next image:
But if I blink a LED with a delay of 1 ms, I got frequency around 2 MHz.
So, please explain to me, what's going on, and why timer works wrong (or where I'm wrong).
The frequency seems correct to me. If your clock is 12MHz and you have an 8 bit PWM your PWM frequency is actually 12MHz/256 = 46.875kHz.
I know there are a lot of examples in internet, but what does need this code to work ?
frecuency oscillator = 4mhz
periode = 0.25us
duty_cicle = 250
Prescale = 16
PR2 = 124
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <pic16f88.h>
#pragma config FOSC = HS // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = ON // Brown-out Detect Enable bit (BOD enabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
void main ()
{
while (1)
{
CCP1CON = 0x2C; /*activate PWM mode*/
PR2 = 0x7C; /*124 (DECIMAL)*/
T2CON = 0X06; /*prescale 16 */
CCPR1L = 0X3E;
}
}
I want to see :
Period of PWM = 2ms
Dutycicle = 1ms
Sincerilly
NIN
First off topic:
Don't include pic16f88.h, it's included by xc.h.
Little more off topic:
If you use a more modern part (e.g. PIC16f1619), you can use the MPLAB Code Configurator to generate the TMR2 and CCP code for you. It'll also cost less and have more flash/ram. That device is on the curiosity board ($20).
On Topic:
Your first stop is the datasheet.
The PWM section has the setup for PWM operation.
Step1:
The timer 2 takes Fosc/4 as an input, which is 1mhz in your case.
Target frequency is 500Hz. 1e6/500 = 2k.
I'd suggest a prescaler of 16, and pr value of 125. This will give you exactly 500Hz.
Step2:
We want a 50% duty cycle. CCP1L floor(125/2) = 62. CCP1X:CCP1Y = 0.5 * 4 = 2.
Step 3:
Clear the tris bit.
Step4 and 5:
Turn it on
// Step 1
TMR2ON = 0;
TOUTPS = 0;
T2CKPS = 2;
PR2 = 250U;
// Step 2
CCP1L = 62U;
CCP1X = 1;
CCP1Y = 0;
// Step 3
TRISB3 = 0;
// Step 4
TMR2ON = 1;
// Step 5
CCP1M = 0xC;
Hope that helps.
The datasheet states:
In Pulse-Width Modulation (PWM) mode, the CCP1 pin
produces up to a 10-bit resolution PWM output. Since
the CCP1 pin is multiplexed with the PORTB data latch,
the TRISB bit must be cleared to make the CCP1
pin an output.
So you must set the TRIS bit for the CCP1 pin to output:
TRISB &= ~(1 << 3); //Clear bit RB3/CCP1 in TRISB, makes PORTB3/CCP1 output.
This assumes CCPMX: CCP1 Pin Selection bit in the configuration word is clear. If set, then CCP1 is on RB0 in stead of RB3 but since I see no mention of CCPMX in your configuration pragma's i assume it is cleared.
I am working with UART of lPC1788,in that I did the following settings for PCLK as 12MHZ and baud rate as 115200 but while I am simulating this code the baud rate is not assigned as 115200 or approx.
uint32_t uart0_init()
{
LPC_SC->PCONP |=(1<<3); //Enabling power/clock for UART0
LPC_IOCON->P0_2 = 1; //Selecting fn. 001 for P0.2(U0_TXD)
LPC_IOCON->P0_3 = 1; //Selecting fn. 001 for P0.3(U0_RXD)
LPC_UART0->LCR =(0x83); //Selecting DLAB=1,1 stop bit,Parity bit and 8-bit character length
LPC_UART0->DLL = 0x04; //For PCLK=12MHZ and baud rate as 115200,DLL=4(in dec.)
LPC_UART0->DLM = 0x00; //For PCLK=12MHZ and baud rate as 115200,DLM=0(in dec.)
LPC_UART0->FDR =(0x85); //DIVADDVAL=1(3:0) and MULVAL=2(7:4) in FDR calculated from the FRest value
LPC_UART0->LCR =(0x03); //Disabling DLAB=0
LPC_UART0->FCR |=(7<<0); //Enable FIFOEN,TXFIFORES and RXFIFORES in FCR(0,1,2)
LPC_UART0->FCR |=(0<<0); //Disable FIFOEN,TXFIFORES and RXFIFORES in FCR(0,1,2)
//NVIC_EnableIRQ(UART0_IRQn);
//LPC_UART0->IER = IER_RBRIE | IER_THREIE | IER_RXIE;
return 1;
}
For me its coming near 384615,its totally different.Is there any calculations to be done to obtain exact of 115200 baud.
Please do clear for me..
Dont use uart init to change system control settings. You will overwrite them elsewhere if you are not careful. Create sysInit function and set it there. Set divider to 10 (0x0a).
LPC_SC->PCLKSEL = 0x0A;
This will divide MCLK with 10 and you will get 12Mhz clock for peripherals. After that you need to set FDR and DLL settings to achieve ~115200 baud.