STM32 change timer frequency each x PWM pulses - Page 1
Forum section: electronics / general

author: WolfK21
author_rank: Contributor
timestamp: July 08, 2022, 02:17:55 pm
content: Hi,first of all, I have to say I'm typically an hardware engineer, that's why my typical projects are normally a bit more hardware focused and my programming skills are not that great.Furthermore I have to say, this is the first time working with an STM32F103RBT6 using the STM CubeIDE and its HAL. Before this I did stuff with various ATmegas (long before Arduino was widely spread) and some PIcs.My project involves an STM32F103RBT microcontroller that uses it's internal ADCs triggered by timer 4 at 15Samples/s.As soon as the ADC buffer is full the DMA complete interrupt is executed and whitin the ISR the processor increases the sample rate to 1MSamples per second and changes the DMA buffer to a fresh one.Overall the sampling should look like this at the end:20000 samples (10 DMA buffers) at 15samples/second200 samples (1 DMA buffer) at 1Msamples/second200 samples (1 DMA buffer) at 500ksamples/second200 samples (1 DMA buffer) at 250ksamples/second200 samples (1 DMA buffer) at 125ksamples/secondand so on until ~15 samples per second are reached.This has to be done so fast that there is no "gap" between two buffers.But as you can see in the picture below the DMA interrupt can't execute fast enough to be able to do that.The picture shows the point where the timer is changed from 15samples/second to 250kS/second (I made this picture with 250kS/s instead of 1MS/s but you will get the point).Blue: Timer4 output (this generates the sample rate for the ADCs)Red: Debug_GPIO set at the beginning of the DMA Transfer Interrupt routineYellow (D12): Debug_GPIO set when the timer gets is new values.Now I'm struggling to get this code even faster. I tried to get rid of the HAL in the time critical sections (resulting in some pretty weird looking code mixture).Are there ways to get this ISR call even faster?If there is now way to speed the ISR up, is there another possibility to create a sampling clock that can quickly change its output frequency on the fly without hickup and delays?Is there a way to generate 200 sample clock pulses at one frequency and than use DMA to give the timer completly different settings to generate another 200 clock pulses at a completly different frequency?I attached this main-file of the source code. Please excuse the crudity of the code, I'm a mostly self taught programmer using trial and error.To make inspecting the code easier:The DMA ISR is in line 316At this point I'm pretty much lost. I hope somebody has a brilliant idea on this topic.Best regards,Wolfgang.Edit: modified the silly MSamples copy&paste error.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 08, 2022, 03:43:25 pm
content: I did not look at the code, because I don't like HAL.What clock speed are you running on?You say it has to be done fast enough that there is no gap between the buffers, but the question here is how big may the "gap" be.When going from 15Sa/s to 1MSa/s does the 1st sample at the higher speed need to be taken directly, or is a micro second delay good enough?It has been a while that I used the F103, but I think there is an option on the timers, that you can load a new value to the registers and later on copy them on a trigger, but not sure if this trigger can be a DMA finished event. This way you can set the next sample rate in the DMA interrupt, and when that cycle finishes it will automatically switch to the new rate. It does not solve the buffer switching, which still has to be done in the interrupt.For stuff like this your best bet might be to go bare metal. Forget the HAL. Read the user manual and program directly on the registers. Use -o2 optimization when compiling.Look at my code here:https://github.com/pecostm32/STM32F103C8_USB_CH340There are lots of comments in there, and it takes care of the clock setup to run on 72MHz provided the external clock is connected to an 8MHz crystal or oscillator. There is also this project that uses DMA, but that is for a F303. Not that different, but check the manual.https://github.com/pecostm32/STM32F303_Sine_Square_Generator

author: Siwastaja
author_rank: Super Contributor
timestamp: July 08, 2022, 04:17:20 pm
content: Consider doing the 15 Hz part in IRQs (or even polling), and then switch to DMA for the 1 MHz part.Also does it need to be exactly 1MHz? You could get close by letting the ADC free-run and configure channels(s) and their sampling time(s) in a suitable way. Then you wouldn't need to use timer for triggering.Or, how about letting the ADC (+ DMA) run all the time at 1 MHz, and just read out the latest value from memory during the 15Hz part and just not care about other data? This only poses some power consumption penalty, and of course memory bus is congested, very slightly decreasing CPU performance.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 08, 2022, 04:55:41 pm
content: Another idea, is to setup your DMA as a circular buffer to hold all the samples, so no buffer swapping in the DMA registers and use the auto reload double buffering feature to switch the sample rate. For this you would need a second timer to count the number of samples. For this you can use the overflow of the timer that generates the sample rate. Having an interrupt on this second timer can take care of setting the next sample rate and the number of samples to count, which is only 20000 for the 15Sa/s and then 200 for the rest of the rates.By using this feature of reloading on an event there is no gap.See chapter 14 of the reference manual:https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf

author: WolfK21
author_rank: Contributor
timestamp: July 08, 2022, 07:06:16 pm
content: Hello,I didn't expect so many answers in such a short time.Quote from: Siwastaja on July 08, 2022, 04:17:20 pmThe frequency should be pretty stable and well known (for the post processing of the data I have to generate a timebase that relies on knowing the actual sampling frequency).I already tried something like that. The probem was that the interrupts took so much time that the routine writing the data to the SD card couldn't keep the pace and the buffer was overwritten before it was able to be stored onto the card.This idea sounds very interestingQuote from: pcprogrammer on July 08, 2022, 04:55:41 pmI think I will try to implement this into the controller. But first I have to figure out how to set all the timers in a way I can get them running together.Meanwhile I found the AN4776 Timer Cook Book, maybe that helps to understand what I need to configure in which register to get it running.As soon as I have it running I will come back and present the solution (or I'm again stuck in new problems...)Best regards,Wolfgang

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 08, 2022, 07:23:17 pm
content: Look at the code in the F303 project I revered to:https://github.com/pecostm32/STM32F303_Sine_Square_GeneratorIt uses timer 2 and 3 plus DMA to generate a sine with the ability to shift the phase of the sine in respect to the square waves. I think you can use a similar timer setup.For writing to the SD card you can use the DMA half way interrupt to trigger the write of the first half to the card and then the full interrupt to trigger the write of the second half to the card. The SD card write process is best done in the main loop. This should be fast enough to keep the process going without data loss until the card is full.

author: wek
author_rank: Frequent Contributor
timestamp: July 10, 2022, 07:43:20 am
content: Quotemmm, hundreds of Msps, ambitious... :-)QuoteThe easiest way is to use the repetition-count register in TIM, and then you probably don't even need DMA to switch frequencies (you still need DMA to pull samples from ADC). Only TIM1 has RCR, though, so you have to switch ADC to be triggered from TIM1.In TIM1, set ARR preload (by setting TIM1_CR1.ARPE) and set TIM1_RCR to 200 and enable Update interrupt. In that interrupt, set the next ARR (and possibly PSC). It will get "active" only after the next Update event, which thanks to RCR is after 200 periods, so the ISR may have maximum latency of that 200 periods.You can of course use also DMA if you want, in the same way, triggered by Update.The nice thing with timers is, that you can enable some of its output to PWM onto a pin, and then you'll see on oscilloscope/LA how it works (or how it fails to work :-) ).This can be pulled out also without TIM1, by a master-slave combination of two timers, but that's more complicated, so why.JWPS. Oh, yes, Cube/HAL. If you can't click your application entirely in CubeMX, just don't use it at all. Trying to "fix it/combine" is like trying to deliberately shoot yourself into foot with a gun which is known to blow up almost certainly.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 10, 2022, 08:06:54 am
content: Quote from: wek on July 10, 2022, 07:43:20 amNice one. Did not think of the repetition counter. It makes things simpler, but there is the problem of the repetition count only being 8 bits, so it won't work for the 20000 samples he want's on the lowest rate.

author: wek
author_rank: Frequent Contributor
timestamp: July 10, 2022, 08:17:02 am
content: QuoteYes, but 20000 = 100*200 is trivial to pull out in the ISR.JW

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 10, 2022, 08:34:13 am
content: That is true. A simple state machine can do the trick. Needs something like this anyway to keep track of which new setting to load to the timer reload value.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 10, 2022, 08:41:45 am
content: You could even use repetition counter to update the counter, and enable DMA request for the update, so that you have another DMA channel which just writes from incrementing memory array to the TIM1->ARR register. If it works out, then you would need no CPU interaction at all to configure new periods. This would scale to the 15Sa/s case, too; with repetition counter fixed to 200, your ARR table would look like this:100 times ARR value for 15Sa/s1 time ARR value for 1MSPS1 time ARR value for 500kspsand so on.

author: WolfK21
author_rank: Contributor
timestamp: July 10, 2022, 10:12:01 am
content: Hello,Quote from: wek on July 10, 2022, 07:43:20 amthis was a dump copy and paste error, but I think you already got the point that the 500, 250 and so on are kilosamples...Quote from: wek on July 10, 2022, 07:43:20 amThis concept sounds very interessting. I think this is even a bit simpler as the one proposed by pcprogrammer.Now I have to understand how to implement this without the HAL.Quote from: wek on July 10, 2022, 07:43:20 amThis is something I've already done, the logic analyzer is a great tool to get a very good impression of whats going on (or not). The PWM output is extremely helpful to see what the timer is doing.Quote from: wek on July 10, 2022, 07:43:20 amYes, I knew already at the beginning that this HAL might bring me into trouble, but as a complete newbie to these STM32s I went that way. Before I already tried to do this completely on my own (bare metal register programming), but I got pretty soon stuck in the pure amount of registers within the clock system.Now it is really hard to completely switch back to bare metal programming as there are alreay many things implemented. The whole SD-card SPI-mode and the USART works completely fine with the HAL, I can get suprisingly high data rates on both of these interfaces without a lot of latency.Therefore I think I will stay with the HAL for all the USART and SPI stuff and do the timer and ADC configuration "by hand".As I have very little spare time at the moment, all this implementation will probably take some weeks. But I will come back and show the final solution at the end.Best regards,Wolfgang

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 10, 2022, 11:06:19 am
content: Quote from: Siwastaja on July 10, 2022, 08:41:45 amDepends on which DMA channels can be used to get this done, but sounds good. Then the only interrupt needs to be for the ADC DMA on half and full to write out the data.Edit: Looks like DMA1 channel 1 for ADC1 and channel 5 for TIM1_UP.

author: WolfK21
author_rank: Contributor
timestamp: July 15, 2022, 12:57:54 pm
content: Hello,after a week of fiddling around on the topic after work, I was able to get the whole thing running.Now I use Timer1 in combination with the RCR register and the Timer1 Update Interrupt.The state machine just calculates the initial Prescaler, ARR and CCR values for Timer1 channel1 and the rest is done during the interrupt. Depending on the current state of the overall state machine the interrupt routine calculates the values for timer1 that will be loaded right before the next interrupt happens.Sadly the ADC1 trigger selection only support Timer1 CC1 and not the Update event as a trigger option, therefore the time between two samples will slightly differ when the update involves a different PSC setting (but this is neglegible as it only happens during relativley low sample rates).To get the data out of the DMA I decided not to use the DMA half complete interrupt. Even if the memory is only half full, the time until it is completely full would be too short to empty the first half onto the SD card.As I have alredy the timer 1 update interrupt coming up every 200 samples I decided to use this interrupt as starting point to write the next 200 samples onto the SD card and the total memory block is 12x200 uint32_t. As the ADC *should* be always only 200 samples ahead of the routine that writes the data to the card, there should be enough space in case the SD card takes more time.In pratice there still seems to be an issue with this concept, as the Values in this file look somehow twisted.The Yellow trace in the Scopes screenshot shows the signal fed into the ADC, the second picture shows what the micro logs.Anyways thanks for your help. Now I will try to cleanup the code and try to find the DMA bug.Best regards,Wolfgang

author: wek
author_rank: Frequent Contributor
timestamp: July 15, 2022, 04:39:42 pm
content: QuoteSet TIM1_CCR1=0. Not good for displaying PWM (for that, use a different channel), but should work as ADC trigger OK.JW

author: WolfK21
author_rank: Contributor
timestamp: July 16, 2022, 04:42:23 pm
content: Hello,I will try this trick, at the moment this is just a minor issue.After a whole day of testing I'm still stuck with some weird DMA issue.I did some debug outputs to see whats going on and plotted these values. These debug outputs are done before the controller writes new data to the SD card, so they are asynchronous to the actual sampling, but for this purpose this doesn't matter.The graph shows the state of the DMA1_Channel1->CNTDR register (actually 2400 - DMA1_Channel1->CNTDR, because it counts backwards so I calculated it to be forwards) in Blue.The yellow and orange plots show the block that will be written to the SD card. Therefore *ideally* the blue graph should be always ahead of the yellow+orange plots (except the DMA rollover).This concept works really well while the controller is sampling at the low speed. For example when the block of samples 200 to 400 is written the DMA counter is "only" at 401.During the fast sampling phase the SD write routine can't keep up with the ADC, therefore there is this huge jump in the DMA counter.After the sampling rate has dropped to lower speeds the really strange thing happens. There is a constant offset ofexactly100 Samples of the DMA counter to the "block boundaries".For me it seems like somewhere the DMA system seems to be too slow and the DMA looses samples.Because as soon as the SD write routing was able to put all the fast samples onto the card, the overall behaviour should be very similar as it was before the fast samling period was done.I did some test where the fastest samplerate was "only" 500kS/s. The plot of the Debug output shows no constant offset between the DMA counter and the SD buffer, even after the fast sampling is over.Does anyone know if there is a "speed limit" for the DMA I've hit, or at least whats going on?Best regards,Wolfgang

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 16, 2022, 04:57:42 pm
content: Do you know how the SD card communication is done? When that is also DMA based there might be a problem there.I don't think 1MSa/s should be a problem for the DMA if it is just the one channel running.This is one of the reasons I don't like HAL and third party code. When there is a problem it is very hard to deduce what the cause is.

author: WolfK21
author_rank: Contributor
timestamp: July 16, 2022, 07:44:42 pm
content: The SPI communication is done without DMA. It's done with HAL but I checked the DMA registers with the debugger to be completely sure.The USART is the only thing that uses DMA too. But during this fast sampling period there in absolutely no communication on the USART (I checked it with the logic analyzer).Furthermore I checked the settings in these DMA channels. They are set so lowest priority, whereas the ADC DMA is set to highest priority.I now tried to enable the DMA Error interrupt and set an GPIO within the interrupt routine, but nothing happens. It seems like DMA doesn't throw any error.Quote from: pcprogrammer on July 16, 2022, 04:57:42 pmYes, you're absolutely right. The problem is that all these Wizards and stuff seem to make programming so easy for us hobby programmers with limited time and rescources. But at the end they will just bite you in the as....Best regardsWolfgang

author: WolfK21
author_rank: Contributor
timestamp: July 17, 2022, 10:10:05 am
content: Hello again,after a night of restless dreams about DMA I think the problem I'm seeing is somehow related to the 1MS/s.I think the problem is that the ADC clock is running at 14MHz ( the max Frequency for the ADC and the only way to achive 1MS/s) and the timer is trying to trigger the ADC at its absolute max. sample rate.I recon this causes the ADC to be triggered only every second trigger event from the timer and therefore it only generates exactly half of the requested samples. This is also why the system works fine as soon as the sample rate is dropped to 500kS/s.To prove my theory I will modify my timer value calculation algorithm a bit to be able the generate not only 1MS and 500kS, but also 900kS. If it works with 900kS this will probably prove the theory :/I will come back with the results....Update:I modified the sampling calculation routine and did some measurements with 1MS and 888kS.The pictures clearly show there's a fundamental difference between 1MS and 888kS. The 888kS measurement has no offset between the DMA counter and the SD write routine after the fast sampling period is over, while the 1MS measurement keeps the constant offset of 100 samples.Furthermore I did some plots for each 200 samples block of the fast sampling routine to see if there's something obvious going on. In the 1MS picture you can clearly see a kink in the center of each plot (seems like it is invisible for the 1us plot due to too low gradients). In the 888kS picture the kinks are gone.After all I think the best thing might be to look for an ADC that is capable of more the 1MS, then I don't have to run the peripherals at the absolute maximum speed which might improve sampling quality too.For now I will stay at 888kS and will probably upgrade later onto an STM32F446RE Nucleo Board.Best regardsWolfgang

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 17, 2022, 11:50:22 am
content: Did you check all the settings to be correct for 1us conversion time?QuoteThis is from the reference manual page 216.In the ADC_SMPR1 and ADC_SMPR2 registers the sample time needs to be set for 1.5 to be able to do 1us conversion.

author: WolfK21
author_rank: Contributor
timestamp: July 17, 2022, 02:03:14 pm
content: Yes, I'm absolutely sure its configured to this. I checked the configuration multiple times, but I seems like it can't simply handle to be triggered at 1MHz.Down below is the code snippet.Code:ADC1->CR1 = ADC_CR1_DUALMOD_1 | ADC_CR1_DUALMOD_2; // Set ADC1 to regular simultaneous dual mode// Select the Timer1_CC1 Interrupt as trigger for the ADC (EXTSEL = 0)// Data are aligned on the right side// Disable continous conversion//ADC1->CR2 &=~ (ADC_CR2_EXTSEL_Msk | ADC_CR2_ALIGN | ADC_CR2_CONT);//ADC2->CR2 &=~ (ADC_CR2_EXTSEL_Msk | ADC_CR2_ALIGN | ADC_CR2_CONT);DMA1_Channel1->CNDTR = ADC_BFR_BLOCKS * ADC_BFR_SIZE;DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;DMA1_Channel1->CMAR = (uint32_t)&ADC_Buffer;// Set DMA channel to very high priority// Memory Size is 32bit (0b10)// Peripheral Size is 32bit (0b10)// Increment in memory// DMA circular modeDMA1_Channel1->CCR = (0x3 << DMA_CCR_PL_Pos)|(0x2<<DMA_CCR_MSIZE_Pos) | (0x2<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_CIRC;// | DMA_CCR_TEIE | DMA_CCR_HTIE | DMA_CCR_TCIE;DMA1_Channel1->CCR |= DMA_CCR_EN;// Enable DMAADC1->SMPR1 = 0x00;// Set all channels (CH10 to CH17) to the lowest possible sampletimeADC1->SMPR2 = 0x00; // Set all channels (CH0 to CH9) to the lowest possible sampletimeADC2->SMPR1 = 0x00;// Set all channels (CH10 to CH17) to the lowest possible sampletimeADC2->SMPR2 = 0x00; // Set all channels (CH0 to CH9) to the lowest possible sampletimeADC1->SQR1 = 0x00;// Set number of conversions to 1 and disable channels 13 to 16ADC1->SQR2 = 0x00;// Disable channels 7 to 12ADC1->SQR3 = 13;// Set the first channel in the sequence to ADC channel 13 (Voltage)ADC2->SQR1 = 0x00;// Set number of conversions to 1 and disable channels 13 to 16ADC2->SQR2 = 0x00;// Disable channels 7 to 12ADC2->SQR3 = 12;// Set the first channel in the sequence to ADC channel 12 (Current)// Enable the conversion start on an external trigger// Enable DMA requestsADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EXTTRIG| ADC_CR2_DMA;ADC2->CR2 = ADC_CR2_ADON | ADC_CR2_EXTTRIG;// Clear pending status flagsADC1->SR = 0;ADC2->SR = 0;Chapter 11.3.6 of the reference manual says:QuoteDoes this mean the EOC flag is set at the 15 clock cycle?

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 17, 2022, 03:25:09 pm
content: Don't think it will be on the 15th clock cycle.The quote from the manual was to show that the CPU clock needs to be at 56MHz to be able to drive the ADC with 14MHz. (56/4=14 72/5=14.4 and thus to high 72/6 is 12 and gives the 1.17us conversion time)For as far as I can see in a quick glance the clock divider setting is not in the code you posted above.Edit: looked at the manual and noticed that the ADC prescaler in RCC_CFGR can only be /2, /4, /6, /8. So the /5 calculation mentioned above is not possible. (Page 102 in the reference manual)

author: WolfK21
author_rank: Contributor
timestamp: July 17, 2022, 04:38:29 pm
content: The clock configuration is still done with the HAL, but I crosschecked it by reading the registers with the debugger:Attached there is also the clock configurators settings (that helped me really a lot to understand whats going on)Code:void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;RCC_OscInitStruct.LSEState = RCC_LSE_ON;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL14;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC;PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV4;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}}Therefore I'm pretty sure the clocks are setup in the right way.I still think its better to move on to a bigger device with more ADC capabilities.

author: pcprogrammer
author_rank: Super Contributor
timestamp: July 17, 2022, 05:17:21 pm
content: It looks correct for 56MHz CPU clock and 14MHz ADC clock.A bit strange that it fails to do the 1MSa/s then. But it is pushing the limits. I wonder how the jyetech scopes do it then, because these dso150 devices claim to do 1MSa/s and are based on a F103 MCU.You could try a F303 device. The manual states that it can do 5MSa/s.Or a F407 or F411, which can do 2.4MSa/s.

author: WolfK21
author_rank: Contributor
timestamp: July 18, 2022, 06:39:21 pm
content: Maybe they just do the freerunning ADC conversion at 14MHz ADC clock, they have most likely no need to switch precisely between sample rates.I think I will take the F303. It has 4 ADCs and really seems to do the 5MSa/s per ADC. This brings me a few new features I could implement in a future Hardware (maybe all 4 ADCs sampling in parallel instead of the 2 I do now).Anyways, thank you all for your help! This is a real great community, I wish I could contribute more in the future.Best regardsWolfgang